from the start... checking in ode-0.9

afrisby
dan miller 2007-10-19 05:24:38 +00:00
parent 0d2e078eeb
commit f205de7847
612 changed files with 202268 additions and 0 deletions

View File

@ -0,0 +1,467 @@
ODE CHANGELOG
-------------
the rules for this file:
* entries are sorted newest-first.
* summarize sets of changes - dont reproduce every CVS log comment here.
* don't ever delete anything.
* keep the format consistent (79 char width, M/D/Y date format).
------------------------------------------------------------------------------
11/03/06 david
* Integrated Christoph Beyer's average based sampling system for body
disabling.
10/26/06 Francisco Leon
* Totally refactored trimesh collision system.
Using GIMPACT instead of OPCODE. Now works correctly, and faster.
Visit http://gimpact.sourceforge.net.
* Finally, test_moving_trimesh.exe works nicely.
* Fixed autodisable system. Now is possible to set bigger sleeping
threshold values and objects won't be sleeping on the air. They will
rest on the floor properly.
* dInitODE function added.
* Is Obligatory to call dInitODE() at the beginning for initialize ODE,
and calling dCloseODE() when the program ends.
09/20/06 bram
* Fixed two bugs in cyl/plane collision test.
09/13/06 remi
* New Rotoide - Prismatic joint type
* dJointGetUniversalAngles for efficient angle retrieval.
08/09/06 david
* Integrated plane2d joint type which constrains bodies to z == 0.
07/06/06 david
* Added heightfield primitive collision code. Simple test available in
ode/test/test_heightfield
04/03/06 rodrigo
* Added Convex primitive collision code,
currently only convex-sphere and convex-plane work
04/01/06 bram
* Added program to test trimesh vs sphere: ode/test/test_basket
03/20/06 jason379
* Added new autogenerated Visual Studio projects, with Premake scripts
03/17/06 bram
* Added plane/cyl intersection test
* Renamed CCylinder to Capsule
02/04/06 gcarlton
* Added support for geom offsets.
10/26/05 rodrigo
* Removed LIBTOOL from autotools since it was not really required.
* Added a target to build ODE as a shared library, this shared
library gets build alongside the static one, no flags required.
10/24/05 tfautre
(Backported patches from STABLE branch, applied by Adam)
* dRandInt changed for a non-double all-int version.
* mics minor fixes and improvements.
04/05/05 tfautre
* Fixed segmentation fault with OPCODE on 64 bits systems.
03/31/05 tfautre
* Fixed timer.cpp compiler error on x86-64 using GCC.
03/29/05 colin
* Added trimesh preprocessing to mark unneeded edges and verts. Also
added support for preprocessed info to the ccylinder-trimesh
collider.
12/07/04 adam
* Important AMotors bugfix
09/22/04 jeff
* Assorted small bugfixes and tweaks for
trimesh_{box,ccylinder,trimesh} collisions
09/21/04 jeff
* added functions to joint.cpp to allow joint attachment to moving
geoms.
* added malloc-based memory allocation in step.cpp & lcp.cpp (turned
on with a #define switch in common.h)
05/29/04 russ
* added joint feedback to the QuickStep solver
05/18/04 russ
* added warm starting to the QuickStep solver
05/18/04 russ
* added the QuickStep solver
* added contact parameter functions.
05/05/04 adam
* use dRandInt instead of rand() in stepfast.
04/21/04 russ
* added auto-disable support from Aras Pranckevicius (with
modifications by russ). this useful feature can speed up
simulation significantly in some cases.
* various internal tidyups.
04/20/04 russ
* changed the meaning of the 'index' argument to dJointGetBody():
it was the only remaining API function that does not respect
dJOINT_REVERSE (spotted by Matthew D. Hancher).
* updated the C++ headers: fixed two minor bugs and added
support for dQuadTreeSpace, dRay, and the dGeom::getSpace() method
(from Matthew D. Hancher).
04/18/04 russ
* changed the way that the dInfinity constant is implemented: now it
is #defined to be one of: FLT_MAX, DBL_MAX, HUGE_VAL, HUGE_VALF, or
a large numeric constant. previously it was a variable that was
exported from the library. this simplifies the configuration and
build process quite a bit, especially in the case of DLLs.
* removed the old, deprecated collision system (geom.cpp,space.cpp,
geom.h,space.h,odecpp_old_collision.h). the ODE_OLD_COLLISION
configuration setting no longer has any meaning.
* removed support for dGeomGroups, which have been deprecated for
a while and are equivalent to 'spaces' anyway.
04/13/04 russ
* bug fix in dMassSetCappedCylinder(), from Matthew D. Hancher.
04/08/04 russ
* added trimesh-CCylinder capability, from Vadim Macagon
<vadim_mcagon@hotmail.com>.
04/04/04 adam
* yet another rewrite of triangle-box collision code, this
time based on code donated by Croteam, ported by asko@jetti.org
and tweaked by Erwin.
04/04/04 adam
* merged trimesh-trimesh collision code by
Jeffrey Smith <jeffreys@Softimage.com>.
* changed it to not break the trimesh interface, fix
some GCC compilation problems, bring it up to date with
ODE changes from 2003-11-15 -> 2004-04-04.
* add ability to drop meshes on meshes in test_moving_trimesh,
not as good as it could be but it's illustrative.
01/16/04 adam
* implement a bunch of ultra-simple TriMesh functions that were
in the headers but not in the code -- patch by
Vadim Macagon <vadim_mcagon@hotmail.com>
* disable temporal coherence on trimeshes by default, since
it has scaleability issues that don't make it a general clear win.
12/01/03 adam
* implement dxHashSpace::collide2(), not particularly efficiently.
11/14/03 adam
* applied several Trimesh fixes and improvements from
Aras Pranckevicius <nearaz@interamotion.com>
10/22/03 adam
* apply Nguyen Binh's work for removing many dSetZero() calls
and some other extraneous initializations.
07/29/03 martin
* added dJointAdd*Torque/Force().
07/10/03 russ
* added the StepFast code, by David Whittaker.
07/02/03 martin
* added dMassSet*Total().
07/01/03 martin
* added joint limits and motors to universal joints.
* reversed the polarity of the dJOINT_REVERSE flag.
06/30/03 russ
* added the TriMesh geom class and the quad tree space to the ODE
core. both of these were developed by Erwin de Vries. added OPCODE
to the ODE distribution, this is required by TriMesh.
06/23/03 martin
* added dGeomSetQuaternion() and dGeomGetQuaternion()
* added dJointGet*Anchor2()
05/07/03 russ
* added dGeomGetSpace().
02/05/03 russ
* added dMassSetCylinder().
12/07/02 russ
* added dAreConnectedExcluding().
11/30/02 russ
* added the ray geom class.
* added the dGeomXXXPointDepth() functions.
* added a collision test infrastructure, and some more tests.
11/24/02 russ
* added support for multiple box-box contacts.
11/10/02 russ
* added new collision system. select between the old/new system by
setting the ODE_OLD_COLLISION variable in config/user-settings.
10/28/02 russ
* fixed two problems in the LCP code to improve the reliability of
the dContactApprox1 contact mode.
* added a FAQ question about rolling bodies getting stuck when they
hit multiple geoms.
09/08/02 russ
* added dClosestLineSegmentPoints().
* implemented dCollideCB().
08/28/02 russ
* added dJointSetFeedback() and dJointGetFeedback().
08/05/02 russ
* added dGeomTransformSetInfo() and dGeomTransformGetInfo().
07/13/02 russ
* added dBodySetForce(), dBodySetTorque(), dWorldImpulseToForce(),
dBodyGetPosRelPoint(), dBodyGetPosRelPoint(), dBodyVectorToWorld(),
dBodyVectorFromWorld().
* added dBodyGetPointVel() (thanks to Colin Reed).
* added a new C++ interface (from Martin C. Martin, with modifications
by russ). the old C++ interface is now in odecpp_old.h.
06/25/02 russ
* added an additional BSD-style licensing option for ODE.
06/23/02 russ
* added dCloseODE(), contributed by Nate Waddoups and David McClurg.
05/16/02 russ
* added dSpaceQuery(), contributed by Nate Waddoups.
04/07/02 russ
* added a section to the documentation for universal joints.
this includes a picture of the joint.
04/05/02 russ
* added a universal joint class (generously contributed by
Martin C. Martin). it doesn't (yet) have a motor or joint limits,
but it does come with tests.
03/11/02 russ
* makefile changes to accomodate OSs with command line length
limitations (thanks to Norman Lin).
01/06/02 russ
* added the dBodySetGravityMode() and dBodyGetGravityMode()
functions, which change the dxBodyNoGravity body flag.
* added support for building a DLL with MSVC - there is now a
msvc-dll target. thanks to Norman Lin for doing this.
12/28/01 russ
* added the dParamCFM joint parameter.
12/24/01 russ
* reworked the build system to make it more cross-platform.
there is now a single top-level makefile and a configurator.c
program. see the INSTALL file for details.
12/04/01 russ
* the "angular motor" joint has been completed, and a new section
has been added to the documentation.
11/26/01 russ
* added a new joint type: "angular motor". using this joint is a good
way to get ball-joint motors and limits. this is work in progress -
it has not been fully implemented or tested yet.
11/22/01 russ
* replaced the mmap()-based joint group stack (stack.cpp) with a
malloc()-based arena stack (obstack.cpp). this will be more
portable and should not impact performance.
11/12/01 russ
* changed the meaning of the 'flags' parameter to dCollide() and
related functions: now the size of the contact buffer is kept in
the lower 16 bits. this change will be backward compatible.
* added dBodyGetFiniteRotationMode() and dBodyGetFiniteRotationAxis().
* added dBodyAddForceAtRelPos() function.
11/11/01 russ
* added the ability to manually enable and disable bodies.
see dBodyEnable(), dBodyDisable(), dBodyIsEnabled().
* fixed a potential bug: when a world is destroyed that contains
joints in joint groups, those joints are marked as "deactivated" in
the joint group, so when the joint group is destroyed they can be
ignored.
* the test_boxstack demo has new options to enable and disable bodies.
* new configuration parameter in config.h: dEFFICIENT_SIZE.
11/11/01 russ
* started the change log for ODE. changes older than today were added
to this file by inspecting the CVS logs.
11/05/01 russ
* added REAL() constructions for floating point numbers, to prevent
many warnings when compiling under VC++.
11/03/01 russ
* added geometry transform class, documented composite objects.
* added collision rule: no contacts if both geoms on the same body.
this is not the best rule, may have to remove this in the future.
* new dMassAdd() function.
* capped cylinder to capped cylinder collision function.
10/31/01 russ
* increase CFM in some demos to make them more robust.
10/29/01 russ
* added new accessor functions.
10/19/01 russ
* added the dJOINT_TWOBODIES flag to the joint, that says it can not
be attached to just one body.
10/12/01 russ
* fixed a collision bug in dCollide() that was causing memory
corruption when multiple contacts were being returned.
10/11/01 russ
* joints can now return m=0 to be "inactive". added a "null" joint
to test this.
10/09/01 russ
* in the LCP solver, try to fail gracefully when s <= 0.
* dAABBTestFn() API change.
10/08/01 russ
* fixed a contact swapping bug in dCollide().
10/07/01 russ
* added capped cylinder geometry object.
09/30/01 russ
* the test_buggy demo now uses geometry groups.
* added a dAABBTestFn field in the geometry classes.
09/29/01 russ
* added geometry groups.
09/20/01 russ
* added finite rotation stuff.

View File

@ -0,0 +1,29 @@
GIMPACT : Geometric tools for VR.
Copyright (c) 2006 , Francisco León.
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 GIMPACT 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.

View File

@ -0,0 +1,502 @@
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.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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.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
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.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View File

@ -0,0 +1,323 @@
#ifndef GIM_BOXPRUNING_H_INCLUDED
#define GIM_BOXPRUNING_H_INCLUDED
/*! \file gim_boxpruning.h
\author Francisco León
*/
/*
-----------------------------------------------------------------------------
This source file is part of GIMPACT Library.
For the latest info, see http://gimpact.sourceforge.net/
Copyright (c) 2006 Francisco Leon. 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.
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 and GIMPACT-LICENSE-BSD.TXT for more details.
-----------------------------------------------------------------------------
*/
#include "GIMPACT/gim_radixsort.h"
#include "GIMPACT/gim_geometry.h"
/*! \defgroup BOX_PRUNNING
\brief
Tools for find overlapping objects on a scenary. These functions sort boxes for faster collisioin queries, using radix sort or quick sort as convenience. See \ref SORTING .
<ul>
<li> For using these collision routines, you must create a \ref GIM_AABB_SET by using this function : \ref gim_aabbset_alloc.
<li> The GIM_AABB_SET objects must be updated on their boxes on each query, and they must be update by calling \ref gim_aabbset_update
<li> Before calling collision functions, you must create a pair set with \ref GIM_CREATE_PAIR_SET
<li> For finding collision pairs on a scene (common space for objects), call \ref gim_aabbset_self_intersections
<li> For finding collision pairs between two box sets , call \ref gim_aabbset_box_collision
<li> After using collision routines, you must destroy the pairset with \ref GIM_DESTROY_PAIR_SET
<li> When the box set is no longer used, you must destroy it by calling \ref gim_aabbset_destroy
</ul>
*/
//! @{
//! Overlapping pair
struct GIM_PAIR
{
GUINT m_index1;
GUINT m_index2;
};
//typedef struct _GIM_PAIR GIM_PAIR;
//! Box container
struct GIM_AABB_SET
{
GUINT m_count;
aabb3f m_global_bound;//!< Global calculated bound of all boxes
aabb3f * m_boxes;
GUINT * m_maxcoords;//!<Upper corners of the boxes, in integer representation
GIM_RSORT_TOKEN * m_sorted_mincoords;//!< sorted min coords (lower corners), with their coord value as the m_key and m_value as the box index
char m_shared;//!< if m_shared == 0 then the memory is allocated and the set must be destroyed, else the pointers are shared and the set should't be destroyed
};
//typedef struct _GIM_AABB_SET GIM_AABB_SET;
//! Function for creating an overlapping pair set
#define GIM_CREATE_PAIR_SET(dynarray) GIM_DYNARRAY_CREATE(GIM_PAIR,dynarray,G_ARRAY_GROW_SIZE)
//! Function for destroying an overlapping pair set
#define GIM_DESTROY_PAIR_SET(dynarray) GIM_DYNARRAY_DESTROY(dynarray)
//! Allocate memory for all aabb set.
void gim_aabbset_alloc(GIM_AABB_SET * aabbset, GUINT count);
//! Destroys the aabb set.
void gim_aabbset_destroy(GIM_AABB_SET * aabbset);
//! Calcs the global bound only
/*!
\pre aabbset must be allocated. And the boxes must be already set.
*/
void gim_aabbset_calc_global_bound(GIM_AABB_SET * aabbset);
//! Sorts the boxes for box prunning.
/*!
1) find the integer representation of the aabb coords
2) Sorts the min coords
3) Calcs the global bound
\pre aabbset must be allocated. And the boxes must be already set.
\param aabbset
\param calc_global_bound If 1 , calcs the global bound
\post If aabbset->m_sorted_mincoords == 0, then it allocs the sorted coordinates
*/
void gim_aabbset_sort(GIM_AABB_SET * aabbset, char calc_global_bound);
//! log(N) Complete box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to the same set.
/*!
\pre aabbset must be allocated and sorted, the boxes must be already set.
\param aabbset Must be sorted. Global bound isn't required
\param collision_pairs Array of GIM_PAIR elements. Must be initialized before (Reserve size ~ 100)
*/
void gim_aabbset_self_intersections_sorted(GIM_AABB_SET * aabbset, GDYNAMIC_ARRAY * collision_pairs);
//! NxN Complete box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to the same set.
/*!
\pre aabbset must be allocated, the boxes must be already set.
\param aabbset Global bound isn't required. Doen't need to be sorted.
\param collision_pairs Array of GIM_PAIR elements. Must be initialized before (Reserve size ~ 100)
*/
void gim_aabbset_self_intersections_brute_force(GIM_AABB_SET * aabbset, GDYNAMIC_ARRAY * collision_pairs);
//! log(N) Bipartite box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to a different set.
/*!
\pre aabbset1 and aabbset2 must be allocated and sorted, the boxes must be already set.
\param aabbset1 Must be sorted, Global bound is required.
\param aabbset2 Must be sorted, Global bound is required.
\param collision_pairs Array of GIM_PAIR elements. Must be initialized before (Reserve size ~ 100)
*/
void gim_aabbset_bipartite_intersections_sorted(GIM_AABB_SET * aabbset1, GIM_AABB_SET * aabbset2, GDYNAMIC_ARRAY * collision_pairs);
//! NxM Bipartite box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to a different set.
/*!
\pre aabbset1 and aabbset2 must be allocated and sorted, the boxes must be already set.
\param aabbset1 Must be sorted, Global bound is required.
\param aabbset2 Must be sorted, Global bound is required.
\param collision_pairs Array of GIM_PAIR elements. Must be initialized before (Reserve size ~ 100)
*/
void gim_aabbset_bipartite_intersections_brute_force(GIM_AABB_SET * aabbset1,GIM_AABB_SET * aabbset2, GDYNAMIC_ARRAY * collision_pairs);
/*
Brute-Force Vs Sorted pruning
Different approaches must be applied when colliding sets with different number of
elements. When sets have less of 100 boxes, is often better to apply force brute
approach instead of sorted methods, because at lowlevel bruteforce routines gives
better perormance and consumes less resources, due of their simplicity.
But when sets are larger, the complexiity of bruteforce increases exponencially.
In the case of large sets, sorted approach is applied. So GIMPACT has the following
strategies:
On Sorting sets:
!) When sets have more of 140 boxes, the boxes are sorted by its coded min coord
and the global box is calculated. But when sets are smaller (less of 140 boxes),
Is convenient to apply brute force approach.
*******************************************************************************/
//! Constant for apply approaches between brute force and sorted pruning on bipartite queries
#define GIM_MIN_SORTED_BIPARTITE_PRUNING_BOXES 600
//! Constant for apply approaches between brute force and sorted pruning for box collision
#define GIM_MIN_SORTED_PRUNING_BOXES 140
//Use these functions for general initialization
//! Initalizes the set. Sort Boxes if needed.
/*!
\pre aabbset must be allocated. And the boxes must be already set.
\post If the set has less of GIM_MIN_SORTED_BIPARTITE_PRUNING_BOXES boxes, only calcs the global box,
else it Sorts the entire set( Only applicable for large sets)
*/
void gim_aabbset_update(GIM_AABB_SET * aabbset);
///Use these functions for general collision
//! Complete box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to the same set.
/*!
This function sorts the set and then it calls to gim_aabbset_self_intersections_brute_force or gim_aabbset_self_intersections_sorted. This is an example of how to use this function:
\code
//Create contact list
GDYNAMIC_ARRAY collision_pairs;
GIM_CREATE_PAIR_SET(collision_pairs);
//Do collision
gim_aabbset_self_intersections(&aabbset,&collision_pairs);
if(collision_pairs.m_size==0)
{
GIM_DYNARRAY_DESTROY(collision_pairs);//
return; //no collisioin
}
//pair pointer
GIM_PAIR *pairs = GIM_DYNARRAY_POINTER(GIM_PAIR,collision_pairs);
GUINT i, ti1,ti2;
for (i=0;i<collision_pairs.m_size; i++)
{
ti1 = pairs[i].m_index1;
ti2 = pairs[i].m_index2;
//Do something with the pairs
....
....
...
}
//Terminate
GIM_DYNARRAY_DESTROY(dummycontacts);
GIM_DYNARRAY_DESTROY(collision_pairs);
\endcode
\param aabbset Set of boxes. Sorting isn't required.
\param collision_pairs Array of GIM_PAIR elements. Must be initialized before (Reserve size ~ 100)
\pre aabbset must be allocated and initialized.
\post If aabbset->m_count >= GIM_MIN_SORTED_PRUNING_BOXES, then it calls to gim_aabbset_sort and then to gim_aabbset_self_intersections_sorted. Global box won't be calculated.
*/
void gim_aabbset_self_intersections(GIM_AABB_SET * aabbset, GDYNAMIC_ARRAY * collision_pairs);
//! Collides two sets. Returns a list of overlapping pairs of boxes, each box of the pair belongs to a different set.
/*!
\pre aabbset1 and aabbset2 must be allocated and updated. See gim_aabbset_update.
\param aabbset1 Must be updated.
\param aabbset2 Must be updated.
\param collision_pairs Array of GIM_PAIR elements. Must be initialized before (Reserve size ~ 100)
*/
void gim_aabbset_bipartite_intersections(GIM_AABB_SET * aabbset1, GIM_AABB_SET * aabbset2, GDYNAMIC_ARRAY * collision_pairs);
///Function for create Box collision result set
#define GIM_CREATE_BOXQUERY_LIST(dynarray) GIM_DYNARRAY_CREATE(GUINT,dynarray,G_ARRAY_GROW_SIZE)
//! Finds intersections between a box and a set. Return the colliding boxes of the set
/*!
\pre aabbset must be allocated and initialized.
\param test_aabb Box for collision query
\param aabbset Set of boxes .Global bound is required.
\param collided Array of GUINT elements, indices of boxes. Must be initialized before (Reserve size ~ 100)
*/
void gim_aabbset_box_collision(aabb3f *test_aabb, GIM_AABB_SET * aabbset, GDYNAMIC_ARRAY * collided);
//! Finds intersections between a box and a set. Return the colliding boxes of the set
/*!
\pre aabbset must be allocated and initialized.
\param vorigin Origin point of ray.
\param vdir Direction vector of ray.
\param tmax Max distance param for ray.
\param aabbset Set of boxes .Global bound is required.
\param collided Array of GUINT elements, indices of boxes. Must be initialized before (Reserve size ~ 100)
*/
void gim_aabbset_ray_collision(vec3f vorigin,vec3f vdir, GREAL tmax, GIM_AABB_SET * aabbset, GDYNAMIC_ARRAY * collided);
/*
For sorting, each box corner must be discretized to a 32 bit integer.
For this, we take the x and z coordinates from the box corner (a vector vec3f)
Then convert the (x,z) pair to an integer. For convenience, we choose an error
constant for converting the coordinates (0.05).
*******************************************************************************/
/**
For fitting the coordinate to an integer, we need to constraint the range of its values. So each coord component (x, z) must lie between 0 and 65536.
20 give us a 0.05 floating point error
*/
#define ERROR_AABB 20.0f
/**
An error of 0.05 allows to make coordinates up to 1638.0f and no less of -1638.0f.
So the maximum size of a room should be about 3276x3276 . Its dimensions must lie between [-1638,1638.0f]
*/
#define MAX_AABB_SIZE 1638.0f
//! Converts a vector coordinate to an integer for box sorting
/*!
\param vx X component
\param vz Z component
\param uint_key a GUINT
*/
#define GIM_CONVERT_VEC3F_GUINT_XZ(vx,vz,uint_key)\
{\
GUINT _z = ((GUINT)(vz*ERROR_AABB))+32768;\
uint_key = ((GUINT)(vx*ERROR_AABB))+32768;\
uint_key = (uint_key<<16) + _z;\
}\
//! Converts a vector coordinate to an integer for box sorting,rounding to the upper int
/*!
\param vx X component
\param vz Z component
\param uint_key a GUINT
*/
#define GIM_CONVERT_VEC3F_GUINT_XZ_UPPER(vx,vz,uint_key)\
{\
GUINT _z = ((GUINT)ceilf(vz*ERROR_AABB))+32768;\
uint_key = ((GUINT)ceilf(vx*ERROR_AABB))+32768;\
uint_key = (uint_key<<16) + _z;\
}\
//! Converts a vector coordinate to an integer for box sorting. Secure clamped
/*!
\param vx X component
\param vz Z component
\param uint_key a GUINT
*/
#define GIM_CONVERT_VEC3F_GUINT_XZ_CLAMPED(vx,vz,uint_key)\
{\
GREAL _cx = CLAMP(vx,-MAX_AABB_SIZE,MAX_AABB_SIZE);\
GREAL _cz = CLAMP(vz,-MAX_AABB_SIZE,MAX_AABB_SIZE);\
GUINT _z = ((GUINT)(_cz*ERROR_AABB))+32768;\
uint_key = ((GUINT)(_cx*ERROR_AABB))+32768;\
uint_key = (uint_key<<16) + _z;\
}\
//! Converts a vector coordinate to an integer for box sorting. Secure clamped, rounded
/*!
\param vx X component
\param vz Z component
\param uint_key a GUINT
*/
#define GIM_CONVERT_VEC3F_GUINT_XZ_UPPER_CLAMPED(vx,vz,uint_key)\
{\
GREAL _cx = CLAMP(vx,-MAX_AABB_SIZE,MAX_AABB_SIZE);\
GREAL _cz = CLAMP(vz,-MAX_AABB_SIZE,MAX_AABB_SIZE);\
GUINT _z = ((GUINT)ceilf(_cz*ERROR_AABB))+32768;\
uint_key = ((GUINT)ceilf(_cx*ERROR_AABB))+32768;\
uint_key = (uint_key<<16) + _z;\
}\
//! @}
#endif // GIM_BOXPRUNING_H_INCLUDED

View File

@ -0,0 +1,115 @@
#ifndef GIM_CONTACT_H_INCLUDED
#define GIM_CONTACT_H_INCLUDED
/*! \file gim_contact.h
\author Francisco León
*/
/*
-----------------------------------------------------------------------------
This source file is part of GIMPACT Library.
For the latest info, see http://gimpact.sourceforge.net/
Copyright (c) 2006 Francisco Leon. 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.
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 and GIMPACT-LICENSE-BSD.TXT for more details.
-----------------------------------------------------------------------------
*/
#include "GIMPACT/gim_geometry.h"
#include "GIMPACT/gim_radixsort.h"
/*! \defgroup CONTACTS
\brief
Functions for managing and sorting contacts resulting from a collision query.
<ul>
<li> Contact lists must be create by calling \ref GIM_CREATE_CONTACT_LIST
<li> After querys, contact lists must be destroy by calling \ref GIM_DYNARRAY_DESTROY
<li> Contacts can be merge for avoid duplicate results by calling \ref gim_merge_contacts
</ul>
*/
//! @{
/// Structure for collision results
struct GIM_CONTACT
{
vec3f m_point;
vec3f m_normal;
GREAL m_depth;//Positive value indicates interpenetration
void * m_handle1;
void * m_handle2;
GUINT m_feature1;//Face number
GUINT m_feature2;//Face number
};
//typedef struct _GIM_CONTACT GIM_CONTACT;
#define CONTACT_DIFF_EPSILON 0.00001f
#define GIM_CALC_KEY_CONTACT(pos,hash)\
{\
GINT _coords[] = {(GINT)(pos[0]*1000.0f+1.0f),(GINT)(pos[1]*1333.0f),(GINT)(pos[2]*2133.0f+3.0f)};\
GUINT _hash=0;\
GUINT *_uitmp = (GUINT *)(&_coords[0]);\
_hash = *_uitmp;\
_uitmp++;\
_hash += (*_uitmp)<<4;\
_uitmp++;\
_hash += (*_uitmp)<<8;\
hash = _hash;\
}\
///Creates a contact list for queries
#define GIM_CREATE_CONTACT_LIST(contact_array) GIM_DYNARRAY_CREATE(GIM_CONTACT,contact_array,100)
#define GIM_PUSH_CONTACT(contact_array, point, normal, deep,handle1, handle2, feat1, feat2)\
{\
GIM_DYNARRAY_PUSH_EMPTY(GIM_CONTACT,contact_array);\
GIM_CONTACT * _last = GIM_DYNARRAY_POINTER_LAST(GIM_CONTACT,contact_array);\
VEC_COPY(_last->m_point,point);\
VEC_COPY(_last->m_normal,normal);\
_last->m_depth = deep;\
_last->m_handle1 = handle1;\
_last->m_handle2 = handle2;\
_last->m_feature1 = feat1;\
_last->m_feature2 = feat2;\
}\
///Receive pointer to contacts
#define GIM_COPY_CONTACTS(dest_contact, source_contact)\
{\
VEC_COPY(dest_contact->m_point,source_contact->m_point);\
VEC_COPY(dest_contact->m_normal,source_contact->m_normal);\
dest_contact->m_depth = source_contact->m_depth;\
dest_contact->m_handle1 = source_contact->m_handle1;\
dest_contact->m_handle2 = source_contact->m_handle2;\
dest_contact->m_feature1 = source_contact->m_feature1;\
dest_contact->m_feature2 = source_contact->m_feature2;\
}\
//! Merges duplicate contacts with minimum depth criterion
void gim_merge_contacts(GDYNAMIC_ARRAY * source_contacts,
GDYNAMIC_ARRAY * dest_contacts);
//! Merges to an unique contact
void gim_merge_contacts_unique(GDYNAMIC_ARRAY * source_contacts,
GDYNAMIC_ARRAY * dest_contacts);
//! @}
#endif // GIM_CONTACT_H_INCLUDED

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,147 @@
#ifndef GIM_MATH_H_INCLUDED
#define GIM_MATH_H_INCLUDED
/*! \file gim_math.h
\author Francisco León
*/
/*
-----------------------------------------------------------------------------
This source file is part of GIMPACT Library.
For the latest info, see http://gimpact.sourceforge.net/
Copyright (c) 2006 Francisco Leon. 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.
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 and GIMPACT-LICENSE-BSD.TXT for more details.
-----------------------------------------------------------------------------
*/
#include <math.h>
#include <float.h>
/*! \defgroup BASIC_TYPES
Basic types and constants
Conventions:
Types starting with G
Constants starting with G_
*/
//! @{
/*! Types */
#define GREAL float
#define GINT long
#define GUINT unsigned long
/*! Constants for integers*/
#define GUINT_BIT_COUNT 32
#define GUINT_EXPONENT 5
#define G_FASTMATH 1
#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 65534
#define G_REAL_INFINITY FLT_MAX
#define G_SIGN_BITMASK 0x80000000
#define G_USE_EPSILON_TEST
#define G_EPSILON 0.0000001f
//! @}
/*! \defgroup MATH_FUNCTIONS
mathematical functions
*/
//! @{
#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 IR(x) ((GUINT&)(x))
//! Signed integer representation of a floating-point value.
#define SIR(x) ((GINT&)(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) ((GREAL&)(x))
#define MAX(a,b) (a<b?b:a)
#define MIN(a,b) (a>b?b:a)
#define MAX3(a,b,c) MAX(a,MAX(b,c))
#define MIN3(a,b,c) MIN(a,MIN(b,c))
#define IS_ZERO(value) (value < G_EPSILON && value > -G_EPSILON)
#define IS_NEGATIVE(value) (value <= -G_EPSILON)
#define IS_POSISITVE(value) (value >= G_EPSILON)
///returns a clamped number
#define CLAMP(number,minval,maxval) (number<minval?minval:(number>maxval?maxval:number))
///Swap numbers
#define 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 - ( IR(va) >> 1);\
isva = 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
GREAL gim_inv_sqrt(GREAL f);
//! Computes sqrtf(x) faster.
/*!
\sa gim_inv_sqrt
*/
GREAL gim_sqrt(GREAL f);
//!Initializes mathematical functions
void gim_init_math();
//! Generates an unit random
GREAL gim_unit_random();
//! @}
#endif // GIM_MATH_H_INCLUDED

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,258 @@
#ifndef GIM_RADIXSORT_H_INCLUDED
#define GIM_RADIXSORT_H_INCLUDED
/*! \file gim_radixsort.h
\author Francisco León.
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. 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.
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 and GIMPACT-LICENSE-BSD.TXT for more details.
-----------------------------------------------------------------------------
*/
#include "GIMPACT/gim_memory.h"
/*! \defgroup SORTING
\brief
Macros for sorting.
*/
//! @{
struct GIM_RSORT_TOKEN
{
GUINT m_key;
GUINT m_value;
};
//typedef struct _GIM_RSORT_TOKEN GIM_RSORT_TOKEN;
//comparator for sorting
#define RSORT_TOKEN_COMPARATOR(x, y) ((int)((x.m_key) - (y.m_key)))
// ---- 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 )
//COMMON FUNCTIONS FOR ACCESSING THE KEY OF AN ELEMENT
//For the type of your array, you need to declare a macro for obtaining the key, like these:
#define SIMPLE_GET_FLOAT32KEY(e,key) {key =(GREAL)(e);}
#define SIMPLE_GET_INTKEY(e,key) {key =(GINT)(e);}
#define SIMPLE_GET_UINTKEY(e,key) {key =(GUINT)(e);}
//For the type of your array, you need to declare a macro for copy elements, like this:
#define SIMPLE_COPY_ELEMENTS(dest,src) {dest = src;}
#define kHist 2048
///Radix sort for unsigned integer keys
#define GIM_RADIX_SORT_RTOKENS(array,sorted,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 GIM_RSORT_TOKEN
#define GIM_RADIX_SORT_ARRAY_TOKENS(array, sorted_tokens, element_count, get_uintkey_macro)\
{\
GIM_RSORT_TOKEN * _unsorted = (GIM_RSORT_TOKEN *) gim_alloc(sizeof(GIM_RSORT_TOKEN )*element_count);\
GUINT _i;\
for (_i=0;_i<element_count;_i++)\
{\
get_uintkey_macro(array[_i],_unsorted[_i].m_key);\
_unsorted[_i].m_value = _i;\
}\
GIM_RADIX_SORT_RTOKENS(_unsorted,sorted_tokens,element_count);\
gim_free(_unsorted,sizeof(GIM_RSORT_TOKEN )*element_count);\
}\
/// Sorts array in place. For generic use
#define GIM_RADIX_SORT(type,array,element_count,get_uintkey_macro,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);\
type * _original_array = (type *) gim_alloc(sizeof(type)*element_count); \
memcpy(_original_array,array,sizeof(type)*element_count);\
GUINT _i;\
for (_i=0;_i<element_count;_i++)\
{\
copy_elements_macro(array[_i],_original_array[_sorted[_i].m_value]);\
}\
gim_free(_original_array,sizeof(type)*element_count);\
gim_free(_sorted,sizeof(GIM_RSORT_TOKEN )*element_count);\
}\
/// Sorts array in place using quick sort
#define GIM_QUICK_SORT_ARRAY(type, array, array_count, comp_macro, exchange_macro) \
{\
GINT _i_, _j_, _p_, _stack_index_, _start_, _end_;\
GINT _start_stack_[64]; \
GINT _end_stack_[64];\
_start_stack_[0] = 0;\
_end_stack_[0] = (array_count);\
_stack_index_ = 1;\
while (_stack_index_ > 0)\
{\
_stack_index_ --;\
_start_ = _start_stack_[_stack_index_];\
_end_ = _end_stack_[_stack_index_];\
while (_end_ - _start_ > 2)\
{\
_p_ = _start_;\
_i_ = _start_ + 1;\
_j_ = _end_ - 1;\
while (_i_<_j_) \
{\
for(; _i_<=_j_ && comp_macro(((array)[_i_]),((array)[_p_]))<=0; _i_++) ;\
if (_i_ > _j_) \
{\
exchange_macro(type, array, _j_, _p_);\
_i_ = _j_;\
}\
else\
{\
for(; _i_<=_j_ && comp_macro(((array)[_j_]),((array)[_p_]))>=0; _j_--) ;\
if (_i_ > _j_) \
{\
exchange_macro(type, array, _j_, _p_);\
_i_ = _j_;\
}\
else if (_i_ < _j_)\
{\
exchange_macro(type, array, _i_, _j_);\
if (_i_+2 < _j_) {_i_++; _j_--;}\
else if (_i_+1 < _j_) _i_++;\
}\
}\
}\
if (_i_-_start_ > 1 && _end_-_j_ > 1) \
{\
if (_i_-_start_ < _end_-_j_-1) \
{\
_start_stack_[_stack_index_] = _j_+1;\
_end_stack_[_stack_index_] = _end_;\
_stack_index_ ++;\
_end_ = _i_;\
}\
else\
{\
_start_stack_[_stack_index_] = _start_;\
_end_stack_[_stack_index_] = _i_;\
_stack_index_ ++;\
_start_ = _j_+1;\
}\
}\
else\
{\
if (_i_-_start_ > 1)\
{\
_end_ = _i_;\
}\
else \
{\
_start_ = _j_+1;\
}\
}\
}\
if (_end_ - _start_ == 2) \
{\
if (comp_macro(((array)[_start_]),((array)[_end_-1])) > 0) \
{\
exchange_macro(type, array, _start_, _end_-1);\
}\
}\
}\
}\
#define GIM_DEF_EXCHANGE_MACRO(type, _array, _i, _j)\
{\
type _e_tmp_ =(_array)[(_i)];\
(_array)[(_i)]=(_array)[(_j)];\
(_array)[(_j)]= _e_tmp_;\
}\
#define GIM_COMP_MACRO(x, y) ((GINT)((x) - (y)))
//! @}
#endif // GIM_RADIXSORT_H_INCLUDED

View File

@ -0,0 +1,111 @@
#ifndef GIM_TRI_CAPSULE_COLLISION_H_INCLUDED
#define GIM_TRI_CAPSULE_COLLISION_H_INCLUDED
/*! \file gim_tri_capsule_collision.h
\author Francisco León
*/
/*
-----------------------------------------------------------------------------
This source file is part of GIMPACT Library.
For the latest info, see http://gimpact.sourceforge.net/
Copyright (c) 2006 Francisco Leon. 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.
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 and GIMPACT-LICENSE-BSD.TXT for more details.
-----------------------------------------------------------------------------
*/
#include "GIMPACT/gim_memory.h"
/*! \addtogroup GEOMETRIC_OPERATIONS
*/
//! @{
//! Capsule struct
struct GIM_CAPSULE_DATA
{
GREAL m_radius;
vec3f m_point1;
vec3f m_point2;
};
//typedef struct _GIM_CAPSULE_DATA GIM_CAPSULE_DATA;
#define CALC_CAPSULE_AABB(capsule,aabb)\
{\
if(capsule.m_point1[0]<capsule.m_point2[0])\
{\
aabb.minX = capsule.m_point1[0] - capsule.m_radius;\
aabb.maxX = capsule.m_point2[0] + capsule.m_radius;\
}\
else\
{\
aabb.minX = capsule.m_point2[0] - capsule.m_radius;\
aabb.maxX = capsule.m_point1[0] + capsule.m_radius;\
}\
if(capsule.m_point1[1]<capsule.m_point2[1])\
{\
aabb.minY = capsule.m_point1[1] - capsule.m_radius;\
aabb.maxY = capsule.m_point2[1] + capsule.m_radius;\
}\
else\
{\
aabb.minY = capsule.m_point2[1] - capsule.m_radius;\
aabb.maxY = capsule.m_point1[1] + capsule.m_radius;\
}\
if(capsule.m_point1[2]<capsule.m_point2[2])\
{\
aabb.minZ = capsule.m_point1[2] - capsule.m_radius;\
aabb.maxZ = capsule.m_point2[2] + capsule.m_radius;\
}\
else\
{\
aabb.minZ = capsule.m_point2[2] - capsule.m_radius;\
aabb.maxZ = capsule.m_point1[2] + capsule.m_radius;\
}\
}\
//! Utility function for find the closest point between a segment and a triangle
/*!
\param triangle
\param s1
\param s2
\param contacts Contains the closest points on the segment (1,2), and the normal points to segment, and m_depth contains the distance
\post The contacts array is not set to 0. It adds aditional contacts
*/
void gim_closest_point_triangle_segment(GIM_TRIANGLE_DATA * triangle, vec3f s1,vec3f s2, GDYNAMIC_ARRAY * contacts);
//! Utility function for find the closest point between a capsule and a triangle
/*!
\param triangle
\param capsule
\param contacts Contains the closest points on the capsule, and the normal points to triangle
\return 1 if the triangle collides the capsule
\post The contacts array is not set to 0. It adds aditional contacts
*/
int gim_triangle_capsule_collision(GIM_TRIANGLE_DATA * triangle, GIM_CAPSULE_DATA * capsule, GDYNAMIC_ARRAY * contacts);
//! @}
#endif // GIM_TRI_CAPSULE_COLLISION_H_INCLUDED

View File

@ -0,0 +1,253 @@
#ifndef GIM_TRI_COLLISION_H_INCLUDED
#define GIM_TRI_COLLISION_H_INCLUDED
/*! \file gim_tri_collision.h
\author Francisco León 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.
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 and GIMPACT-LICENSE-BSD.TXT for more details.
-----------------------------------------------------------------------------
*/
/*! \addtogroup GEOMETRIC_OPERATIONS
*/
//! @{
#define MAX_TRI_CLIPPING 8
//! Clips a polygon by a plane
#define PLANE_CLIP_POLYGON(plane,polygon_points,polygon_point_count,clipped,clipped_count,max_clipped) \
{ \
clipped_count = 0; \
GUINT _i, _vi, _prevclassif=32000, _classif; \
GREAL _d; \
for(_i=0;_i<=polygon_point_count;_i++) \
{ \
_vi = _i%polygon_point_count; \
_d = DISTANCE_PLANE_POINT(plane,polygon_points[_vi]); \
_classif = _d>G_EPSILON ?1:0; \
if(_classif == 0) \
{ \
if(_prevclassif==1) \
{\
if(clipped_count<max_clipped) \
{\
PLANE_CLIP_SEGMENT(polygon_points[_i-1],polygon_points[_vi],plane,clipped[clipped_count]); \
clipped_count++; \
} \
} \
if(clipped_count<max_clipped&&_i<polygon_point_count) \
{ \
VEC_COPY(clipped[clipped_count],polygon_points[_vi]); \
clipped_count++; \
} \
} \
else \
{ \
if(_prevclassif==0) \
{ \
if(clipped_count<max_clipped) \
{ \
PLANE_CLIP_SEGMENT(polygon_points[_i-1],polygon_points[_vi],plane,clipped[clipped_count]); \
clipped_count++; \
} \
} \
} \
_prevclassif = _classif; \
} \
}\
struct GIM_TRIPLANES_CACHE
{
/*!
Planes are:
0 : Face normal plane (0,3)
1 : Edge 1 plane (4,7)
2 : Edge 2 plane (8,11)
3 : Edge 3 plane (12,15)
*/
vec4f m_planes[4];
};
//typedef struct _GIM_TRIPLANES_CACHE GIM_TRIPLANES_CACHE;
struct GIM_TRIANGLE_DATA
{
vec3f m_vertices[3];
GIM_TRIPLANES_CACHE m_planes;
};
//typedef struct _GIM_TRIANGLE_DATA GIM_TRIANGLE_DATA;
//! tri_data is a GIM_TRIANGLE_DATA
#define GIM_CALC_TRIANGLE_DATA_PLANES(tri_data)\
{\
TRIANGLE_PLANE((tri_data).m_vertices[0],(tri_data).m_vertices[1],(tri_data).m_vertices[2],(tri_data).m_planes.m_planes[0]);\
EDGE_PLANE((tri_data).m_vertices[0],(tri_data).m_vertices[1],((tri_data).m_planes.m_planes[0]),((tri_data).m_planes.m_planes[1]));\
EDGE_PLANE((tri_data).m_vertices[1],(tri_data).m_vertices[2],((tri_data).m_planes.m_planes[0]),((tri_data).m_planes.m_planes[2]));\
EDGE_PLANE((tri_data).m_vertices[2],(tri_data).m_vertices[0],((tri_data).m_planes.m_planes[0]), ((tri_data).m_planes.m_planes[3]));\
}\
//Structure for collision
struct GIM_TRIANGLE_CONTACT_DATA
{
GREAL m_penetration_depth;
GUINT m_point_count;
vec3f m_separating_normal;
vec3f m_points[MAX_TRI_CLIPPING];
};
//typedef struct _GIM_TRIANGLE_CONTACT_DATA GIM_TRIANGLE_CONTACT_DATA;
struct GIM_TRIANGLE_RAY_CONTACT_DATA
{
GREAL u;
GREAL v;
GREAL tparam;
GUINT m_face_id;
vec3f m_point;
vec3f m_normal;
};
//typedef struct _GIM_TRIANGLE_RAY_CONTACT_DATA GIM_TRIANGLE_RAY_CONTACT_DATA;
//! Fast Triangle Triangle overlapping test
int gim_triangle_triangle_overlap(
GIM_TRIANGLE_DATA *tri1,
GIM_TRIANGLE_DATA *tri2);
//! Fast but inacurate conservative Triangle Triangle overlapping test
int gim_triangle_triangle_overlap_fast(
GIM_TRIANGLE_DATA *tri1,
GIM_TRIANGLE_DATA *tri2);
//! Finds the contact points from a collision of two triangles
/*!
Returns the contact points, the penetration depth and the separating normal of the collision
between two triangles. The normal is pointing toward triangle 1 from triangle 2
*/
int gim_triangle_triangle_collision(
GIM_TRIANGLE_DATA *tri1,
GIM_TRIANGLE_DATA *tri2,
GIM_TRIANGLE_CONTACT_DATA * contact_data);
//Ray triangle
/*!
Solve the System for u,v parameters:
u*axe1[i1] + v*axe2[i1] = vecproj[i1]
u*axe1[i2] + v*axe2[i2] = vecproj[i2]
sustitute:
v = (vecproj[i2] - u*axe1[i2])/axe2[i2]
then the first equation in terms of 'u':
--> 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
*/
#define TRIANGLE_GET_UVPARAMETERS(point,vec1,vec2,vec3,tri_plane,u,v,outside)\
{\
vec3f _axe1, _axe2, _vecproj;\
VEC_DIFF(_axe1,vec2,vec1);\
VEC_DIFF(_axe2,vec3,vec1);\
VEC_DIFF(_vecproj,point,vec1);\
GUINT _i1,_i2;\
PLANE_MINOR_AXES(tri_plane, _i1, _i2);\
if(fabsf(_axe2[_i2])<G_EPSILON)\
{\
u = (_vecproj[_i2]*_axe2[_i1] - _vecproj[_i1]*_axe2[_i2]) /(_axe1[_i2]*_axe2[_i1] - _axe1[_i1]*_axe2[_i2]);\
v = (_vecproj[_i1] - u*_axe1[_i1])/_axe2[_i1];\
}\
else\
{\
u = (_vecproj[_i1]*_axe2[_i2] - _vecproj[_i2]*_axe2[_i1]) /(_axe1[_i1]*_axe2[_i2] - _axe1[_i2]*_axe2[_i1]);\
v = (_vecproj[_i2] - u*_axe1[_i2])/_axe2[_i2];\
}\
if(u<-G_EPSILON)\
{\
outside = 1;\
}\
else if(v<-G_EPSILON)\
{\
outside = 1;\
}\
else\
{\
float sumuv;\
sumuv = u+v;\
if(sumuv<-G_EPSILON)\
{\
outside = 1;\
}\
else if(sumuv-1.0f>G_EPSILON)\
{\
outside = 1;\
}\
else\
{\
outside = 0;\
}\
}\
}\
//! Finds the collision of a ray and a triangle.
#define RAY_TRIANGLE_INTERSECTION(vOrigin,vDir,vec1,vec2,vec3,tri_plane,pout,u,v,tparam,tmax,does_intersect)\
{\
RAY_PLANE_COLLISION(tri_plane,vDir,vOrigin,pout,tparam,does_intersect);\
if(does_intersect != 0)\
{\
if(tparam<-G_EPSILON||tparam>tmax+G_EPSILON)\
{\
does_intersect = 0;\
}\
else\
{\
TRIANGLE_GET_UVPARAMETERS(pout,vec1,vec2,vec3,tri_plane,u,v,does_intersect);\
does_intersect = !does_intersect;\
}\
}\
}\
//! @}
#endif // GIM_TRI_COLLISION_H_INCLUDED

View File

@ -0,0 +1,51 @@
#ifndef GIM_TRI_SPHERE_COLLISION_H_INCLUDED
#define GIM_TRI_SPHERE_COLLISION_H_INCLUDED
/*! \file gim_tri_sphere_collision.h
\author Francisco León
*/
/*
-----------------------------------------------------------------------------
This source file is part of GIMPACT Library.
For the latest info, see http://gimpact.sourceforge.net/
Copyright (c) 2006 Francisco Leon. 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.
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 and GIMPACT-LICENSE-BSD.TXT for more details.
-----------------------------------------------------------------------------
*/
/*! \addtogroup GEOMETRIC_OPERATIONS
*/
//! @{
//! Finds the contact points from a collision of a triangle and a sphere
/*!
\param tri
\param center
\param radius
\param contact_data Contains the closest points on the Sphere, and the normal is pointing to triangle
*/
int gim_triangle_sphere_collision(
GIM_TRIANGLE_DATA *tri,
vec3f center, GREAL radius,
GIM_TRIANGLE_CONTACT_DATA * contact_data);
//! @}
#endif // GIM_TRI_SPHERE_COLLISION_H_INCLUDED

View File

@ -0,0 +1,539 @@
#ifndef GIM_TRIMESH_H_INCLUDED
#define GIM_TRIMESH_H_INCLUDED
/*! \file gim_trimesh.h
\author Francisco León
*/
/*
-----------------------------------------------------------------------------
This source file is part of GIMPACT Library.
For the latest info, see http://gimpact.sourceforge.net/
Copyright (c) 2006 Francisco Leon. 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.
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 and GIMPACT-LICENSE-BSD.TXT for more details.
-----------------------------------------------------------------------------
*/
#include "GIMPACT/gim_boxpruning.h"
#include "GIMPACT/gim_contact.h"
///MAsk defines
#define GIM_TRIMESH_TRANSFORMED_REPLY 1
#define GIM_TRIMESH_NEED_UPDATE 2
/*! \addtogroup TRIMESH
\brief
A Trimesh is the basic geometric structure for representing solid objects.
<p><strong>CREATING TRIMESHES</strong></p>
<ul>
<li> For creating trimeshes, you must initialize Buffer managers by calling \ref gimpact_init
<li> Then you must define the vertex and index sources by creating them with \ref BUFFER_ARRAYS routines, and then call \ref gim_trimesh_create_from_arrays.
<li> An alternative way for creaing trimesh objects is calling \ref gim_trimesh_create_from_data.
<li> For access to the trimesh data (vertices, triangle indices), you must call \ref gim_trimesh_locks_work_data , and \ref gim_trimesh_unlocks_work_data for finish the access.
<li> Each time when the trimesh data is modified, you must call \ref gim_trimesh_update after.
<li> When a trimesh is no longer needed, you must call \ref gim_trimesh_destroy.
</ul>
<p>This is an example of how to create a deformable trimesh that shares vertices with the user application:</p>
\code
//Declaration of vertices
vec3f trimeshvertices[200];
//Declaration of indices
GUINT trimeshindices[100];
... Initializing vertices and triangle indices at beginning
//Then create trimesh
GIM_TRIMESH mytrimesh;
//Calling trimesh create function
gim_trimesh_create_from_data(
&mytrimesh,
trimeshvertices,200,
0 ,//copy_vertices is 0
trimeshindices,
100,
0, //copy_indices is 0
0 //transformed_reply is 0
);
\endcode
<p>Note that parameter transformed_reply is 0, that means that m_transformed_vertex_buffer is a reference to m_source_vertex on the trimesh, and transformations are not avaliable. Use that configuration if you have to simulate a deformable trimesh like cloth or elastic bodies.</p>
<p>When the trimesh is no longer needed, destroy it safely with gim_trimesh_destroy()</p>
<p><strong>UPDATING TRIMESHES</strong></p>
<p>On simulation loops, is needed to update trimeshes every time for update vertices althought updating triangle boxes and planes cache. There is two ways for update trimeshes: </p>
<ul>
<li> Updating vertices directly. You need to access to the \ref GIM_TRIMESH.m_source_vertex_buffer member; a vertex buffer which has access to the source vertices.
\code
// Access to the source vertices
gim_buffer_array_lock(&mytrimesh.m_source_vertex_buffer, G_MA_READ_WRITE);
//Get a pointer to the vertex buffer
vec3f * vertexpointer = GIM_BUFFER_ARRAY_POINTER(vec3f,mytrimesh.m_source_vertex_buffer,0);
//Get the amount of vertices
int veccount = mytrimesh.m_source_vertex_buffer.m_element_count;
//Modify vertices
for (int i=0;i<veccount ;i++ )
{
.....
.....
processing vertices
.....
.....
}
// Don't forget to unlock the source vertex array
gim_buffer_array_unlock(&mytrimesh.m_source_vertex_buffer);
// Notify that the state of the trimesh is changed
gim_trimesh_post_update(&mytrimesh.m_source_vertex_buffer);
\endcode
For making trimeshes that allow to update their vertices, use \ref gim_trimesh_create_from_data with parameter <strong>transformed_reply</strong> = 0.
</ul>
<ul>
<li> Aplying a transformation. Simply use \ref gim_trimesh_set_tranform . Remember that with this method trimeshes must be created with \ref gim_trimesh_create_from_data with parameter <strong>transformed_reply</strong> = 1.
</ul>
<p> After updating vertices, you must call \ref gim_trimesh_update()</p>
<p><strong>TRIMESHES COLLISION</strong></p>
<p>Before collide trimeshes, you need to update them first.</p>
<p>Then you must use \ref gim_trimesh_trimesh_collision().</p>
*/
//! @{
//! Prototype for updating vertices
typedef void * gim_update_trimesh_function(struct _GIM_TRIMESH *);
//! Trimesh
struct GIM_TRIMESH
{
///Original
//@{
GBUFFER_ARRAY m_source_vertex_buffer;//!< Buffer of vec3f coordinates
//! (GUINT) Indices of triangles,groups of three elements.
/*!
Array of GUINT. Triangle indices. Each triple contains indices of the vertices for each triangle.
\invariant must be aligned
*/
GBUFFER_ARRAY m_tri_index_buffer;
//@}
///Allocated
//@{
char m_mask;//!< Don't use directly
//! Allocated transformed vertices vec3f
/*!
Array of vec3f.If gim_trimesh_has_tranformed_reply(this) == 1 then it refers to the m_source_vertex_buffer
\invariant must be aligned
*/
GBUFFER_ARRAY m_transformed_vertex_buffer;
//@}
///Auxiliary data
//@{
GIM_AABB_SET m_aabbset;
GDYNAMIC_ARRAY m_planes_cache_buffer;//! Allocated GIM_TRIPLANES_CACHE
GDYNAMIC_ARRAY m_planes_cache_bitset;
gim_update_trimesh_function * m_update_callback;//! If null, then m_transform is applied.
mat4f m_transform;
//@}
};
//typedef struct _GIM_TRIMESH GIM_TRIMESH;
/// Info about mesh
//! Return the trimesh triangle count
GUINT gim_trimesh_get_triangle_count(GIM_TRIMESH * trimesh);
//! Returns 1 if the m_transformed_vertex_buffer is a reply of m_source_vertex_buffer
char gim_trimesh_has_tranformed_reply(GIM_TRIMESH * trimesh);
//! Returns 1 if the trimesh needs to update their aabbset and the planes cache.
char gim_trimesh_needs_update(GIM_TRIMESH * trimesh);
//! Change the state of the trimesh for force it to update
/*!
Call it after made changes to the trimesh.
\post gim_trimesh_need_update(trimesh) will return 1
\sa gim_trimesh_needs_update,gim_trimesh_has_tranformed_reply
*/
void gim_trimesh_post_update(GIM_TRIMESH * trimesh);
//! Creates the aabb set and the triangles cache
/*!
\param trimesh
\param vertex_array
\param triindex_array
\param transformed_reply If 1, then the m_transformed_vertices is a reply of the source vertices. Else it just be a reference to the original array.
\post it copies the arrays by reference, and creates the auxiliary data (m_aabbset,m_planes_cache_buffer)
*/
void gim_trimesh_create_from_arrays(GIM_TRIMESH * trimesh, GBUFFER_ARRAY * vertex_array, GBUFFER_ARRAY * triindex_array,char transformed_reply);
//! Create a trimesh from vertex array and an index array
/*!
\param trimesh An uninitialized GIM_TRIMESH structure
\param vertex_array A buffer to a vec3f array
\param vertex_count
\param triindex_array
\param index_count
\param copy_vertices If 1, it copies the source vertices in another buffer. Else (0) it constructs a reference to the data.
\param copy_indices If 1, it copies the source vertices in another buffer. Else (0) it constructs a reference to the data.
\param transformed_reply If 1, then the m_transformed_vertices is a reply of the source vertices. Else it just be a reference to the original array. Use 1 if you will apply transformations to the trimesh. See \ref gim_trimesh_set_tranform().
*/
void gim_trimesh_create_from_data(GIM_TRIMESH * trimesh, vec3f * vertex_array, GUINT vertex_count,char copy_vertices, GUINT * triindex_array, GUINT index_count,char copy_indices,char transformed_reply);
//! Clears auxiliary data and releases buffer arrays
void gim_trimesh_destroy(GIM_TRIMESH * trimesh);
//! Copies two meshes
/*!
\param source_trimesh
\param dest_trimesh
\param copy_by_reference If 1, it attach a reference to the source vertices, else it copies the vertices
\param transformed_reply If 1, transformed vertices are reply of source vertives. 1 Is recommended
*/
void gim_trimesh_copy(GIM_TRIMESH * source_trimesh,GIM_TRIMESH * dest_trimesh, char copy_by_reference, char transformed_reply);
//! Locks the trimesh for working with it
/*!
\post locks m_tri_index_buffer and m_transformed_vertex_buffer.
\param trimesh
*/
void gim_trimesh_locks_work_data(GIM_TRIMESH * trimesh);
//! unlocks the trimesh
/*!
\post unlocks m_tri_index_buffer and m_transformed_vertex_buffer.
\param trimesh
*/
void gim_trimesh_unlocks_work_data(GIM_TRIMESH * trimesh);
//! Updates m_transformed_vertex_buffer
/*!
\pre m_transformed_vertex_buffer must be unlocked
*/
void gim_trimesh_update_vertices(GIM_TRIMESH * trimesh);
//! Updates m_aabbset and m_planes_cache_bitset
/*!
\pre gim_trimesh_locks_work_data must be called before
*/
void gim_trimesh_update_aabbset(GIM_TRIMESH * trimesh);
//! Calls before perfom collisions. Updates the trimesh if needed
/*!
\post If gim_trimesh_needs_update returns 1, then it calls gim_trimesh_update_vertices and gim_trimesh_update_aabbset
*/
void gim_trimesh_update(GIM_TRIMESH * trimesh);
//! Set the transform of a trimesh
/*!
\post This function calls to gim_trimesh_post_update
*/
void gim_trimesh_set_tranform(GIM_TRIMESH * trimesh, mat4f transform);
//! Fetch triangle data
/*!
\pre gim_trimesh_locks_work_data must be called before
*/
void gim_trimesh_get_triangle_data(GIM_TRIMESH * trimesh, GUINT triangle_index, GIM_TRIANGLE_DATA * tri_data);
//! Fetch triangle vertices
/*!
\pre gim_trimesh_locks_work_data must be called before
*/
void gim_trimesh_get_triangle_vertices(GIM_TRIMESH * trimesh, GUINT triangle_index, vec3f v1,vec3f v2,vec3f v3);
//! Trimesh Trimesh Collisions
/*!
Before use this function you must update each trimesh:
\code
gim_trimesh_update(TriMesh1);
gim_trimesh_update(TriMesh2);
\endcode
Then you must use the trimesh collision in this way:
\code
int collide_trimeshes(GIM_TRIMESH * TriMesh1, GIM_TRIMESH * TriMesh2)
{
//Create contact list
GDYNAMIC_ARRAY trimeshcontacts;
GIM_CREATE_CONTACT_LIST(trimeshcontacts);
//Collide trimeshes
gim_trimesh_trimesh_collision(TriMesh1,TriMesh2,&trimeshcontacts);
if(trimeshcontacts.m_size == 0) //do nothing
{
GIM_DYNARRAY_DESTROY(trimeshcontacts);//clean contact array
return 0;
}
//Getting a pointer to the contact array
GIM_CONTACT * ptrimeshcontacts = GIM_DYNARRAY_POINTER(GIM_CONTACT,trimeshcontacts);
int contactcount = trimeshcontacts.m_size;
int i;
//Process contacts
for (i=0;i<contactcount ;i++)
{
//Do something with the contact (ptrimeshcontacts)
......
......
// Like creating joints or anything else
......
......
ptrimeshcontacts++;
}
GIM_DYNARRAY_DESTROY(trimeshcontacts);
return contactcount;
}
\endcode
In each contact
<ul>
<li> m_handle1 points to trimesh1.
<li> m_handle2 points to trimesh2.
<li> m_feature1 Is a triangle index of trimesh1.
<li> m_feature2 Is a triangle index of trimesh2.
</ul>
\param trimesh1 Collider
\param trimesh2 Collidee
\param contacts A GIM_CONTACT array. Must be initialized
*/
void gim_trimesh_trimesh_collision(GIM_TRIMESH * trimesh1, GIM_TRIMESH * trimesh2, GDYNAMIC_ARRAY * contacts);
//! Trimesh Sphere Collisions
/*!
Before use this function you must update the trimesh:
\code
gim_trimesh_update(trimesh);
\endcode
Then you must use this function in this way:
\code
int collide_trimesh_sphere(GIM_TRIMESH * trimesh, vec3f center,GREAL radius)
{
//Create contact list
GDYNAMIC_ARRAY trimeshcontacts;
GIM_CREATE_CONTACT_LIST(trimeshcontacts);
//Collide trimeshes
gim_trimesh_sphere_collision(trimesh,center,radius,&trimeshcontacts);
if(trimeshcontacts.m_size == 0) //do nothing
{
GIM_DYNARRAY_DESTROY(trimeshcontacts);//clean contact array
return 0;
}
//Getting a pointer to the contact array
GIM_CONTACT * ptrimeshcontacts = GIM_DYNARRAY_POINTER(GIM_CONTACT,trimeshcontacts);
int contactcount = trimeshcontacts.m_size;
int i;
//Process contacts
for (i=0;i<contactcount ;i++)
{
//Do something with the contact (ptrimeshcontacts)
......
......
// Like creating joints or anything else
......
......
ptrimeshcontacts++;
}
GIM_DYNARRAY_DESTROY(trimeshcontacts);
return contactcount;
}
\endcode
In each contact
<ul>
<li> m_handle1 points to trimesh.
<li> m_handle2 points to NULL.
<li> m_feature1 Is a triangle index of trimesh.
</ul>
\param trimesh
\param center
\param radius
\param contacts A GIM_CONTACT array. Must be initialized
*/
void gim_trimesh_sphere_collision(GIM_TRIMESH * trimesh,vec3f center,GREAL radius, GDYNAMIC_ARRAY * contacts);
//! Trimesh Capsule collision
/*!
Find the closest primitive collided by the ray.
Before use this function you must update the trimesh:
\code
gim_trimesh_update(trimesh);
\endcode
Then you must use this function in this way:
\code
int collide_trimesh_capsule(GIM_TRIMESH * trimesh, GIM_CAPSULE_DATA * capsule)
{
//Create contact list
GDYNAMIC_ARRAY trimeshcontacts;
GIM_CREATE_CONTACT_LIST(trimeshcontacts);
//Collide trimeshes
gim_trimesh_capsule_collision(trimesh,capsule,&trimeshcontacts);
if(trimeshcontacts.m_size == 0) //do nothing
{
GIM_DYNARRAY_DESTROY(trimeshcontacts);//clean contact array
return 0;
}
//Getting a pointer to the contact array
GIM_CONTACT * ptrimeshcontacts = GIM_DYNARRAY_POINTER(GIM_CONTACT,trimeshcontacts);
int contactcount = trimeshcontacts.m_size;
int i;
//Process contacts
for (i=0;i<contactcount ;i++)
{
//Do something with the contact (ptrimeshcontacts)
......
......
// Like creating joints or anything else
......
......
ptrimeshcontacts++;
}
GIM_DYNARRAY_DESTROY(trimeshcontacts);
return contactcount;
}
\endcode
In each contact
<ul>
<li> m_handle1 points to trimesh.
<li> m_handle2 points to NULL.
<li> m_feature1 Is a triangle index of trimesh.
</ul>
\param trimesh
\param capsule
\param contacts A GIM_CONTACT array. Must be initialized
*/
void gim_trimesh_capsule_collision(GIM_TRIMESH * trimesh, GIM_CAPSULE_DATA * capsule, GDYNAMIC_ARRAY * contacts);
///Function for create Trimesh Plane collision result
#define GIM_CREATE_TRIMESHPLANE_CONTACTS(dynarray) GIM_DYNARRAY_CREATE(vec4f,dynarray,G_ARRAY_GROW_SIZE)
//! Trimesh Plane Collisions
/*!
Before use this function you must update the trimesh:
\code
gim_trimesh_update(trimesh);
\endcode
Then you must use this function in this way:
\code
int collide_trimesh_plane(GIM_TRIMESH * trimesh, vec4f plane)
{
//Create contact list
GDYNAMIC_ARRAY tri_plane_contacts;
GIM_CREATE_TRIMESHPLANE_CONTACTS(tri_plane_contacts);
//Collide trimeshes
gim_trimesh_plane_collision(trimesh,plane,&tri_plane_contacts);
if(tri_plane_contacts.m_size == 0) //do nothing
{
GIM_DYNARRAY_DESTROY(tri_plane_contacts);//clean contact array
return 0;
}
//Getting a pointer to the contact array
vec4f * planecontacts = GIM_DYNARRAY_POINTER(vec4f,tri_plane_contacts);
int contactcount = tri_plane_contacts.m_size;
int i;
//Process contacts
for (i=0;i<contactcount ;i++)
{
vec3f contactpoint;
GREAL contactdis;
VEC_COPY(contactpoint,planecontacts[i]); //Get contact point
contactdis = planecontacts[i][3]; // Get distance depth
//Do something with the contact
......
......
// Like creating joints or anything else
......
......
}
GIM_DYNARRAY_DESTROY(tri_plane_contacts);
return contactcount;
}
\endcode
In each contact the 3 first coordinates refers to the contact point, the fourth refers to the distance depth and the normal is the normal of the plane.
\param trimesh
\param plane vec4f plane
\param contacts A vec4f array. Must be initialized (~100). Each element have the coordinate point in the first 3 elements, and vec4f[3] has the penetration depth.
*/
void gim_trimesh_plane_collision(GIM_TRIMESH * trimesh,vec4f plane, GDYNAMIC_ARRAY * contacts);
//! Trimesh Ray Collisions
/*!
\param trimesh
\param origin
\param dir
\param tmax
\param contact
\return 1 if the ray collides, else 0
*/
int gim_trimesh_ray_collision(GIM_TRIMESH * trimesh,vec3f origin,vec3f dir, GREAL tmax, GIM_TRIANGLE_RAY_CONTACT_DATA * contact);
//! Trimesh Ray Collisions closest
/*!
Find the closest primitive collided by the ray
\param trimesh
\param origin
\param dir
\param tmax
\param contact
\return 1 if the ray collides, else 0
*/
int gim_trimesh_ray_closest_collision(GIM_TRIMESH * trimesh,vec3f origin,vec3f dir, GREAL tmax, GIM_TRIANGLE_RAY_CONTACT_DATA * contact);
//! @}
#endif // GIM_TRIMESH_H_INCLUDED

View File

@ -0,0 +1,45 @@
#ifndef GIMPACT_H_INCLUDED
#define GIMPACT_H_INCLUDED
/*! \file gimpact.h
\author Francisco León
*/
/*
-----------------------------------------------------------------------------
This source file is part of GIMPACT Library.
For the latest info, see http://gimpact.sourceforge.net/
Copyright (c) 2006 Francisco Leon. 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.
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 and GIMPACT-LICENSE-BSD.TXT for more details.
-----------------------------------------------------------------------------
*/
#include "GIMPACT/gim_trimesh.h"
/*! \defgroup GIMPACT_INIT
*/
//! @{
//! Call this for initialize GIMPACT system structures.
void gimpact_init();
//! Call this for clean GIMPACT system structures.
void gimpact_terminate();
//! @}
#endif // GIMPACT_H_INCLUDED

View File

@ -0,0 +1,514 @@
/*
-----------------------------------------------------------------------------
This source file is part of GIMPACT Library.
For the latest info, see http://gimpact.sourceforge.net/
Copyright (c) 2006 Francisco Leon. 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.
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 and GIMPACT-LICENSE-BSD.TXT for more details.
-----------------------------------------------------------------------------
*/
#include "GIMPACT/gim_boxpruning.h"
//! Allocate memory for all aabb set.
void gim_aabbset_alloc(GIM_AABB_SET * aabbset, GUINT count)
{
aabbset->m_count = count;
aabbset->m_boxes = (aabb3f *)gim_alloc(sizeof(aabb3f)*count);
if(count<GIM_MIN_SORTED_BIPARTITE_PRUNING_BOXES)
{
aabbset->m_maxcoords = 0;
aabbset->m_sorted_mincoords = 0;
}
else
{
aabbset->m_maxcoords = (GUINT *)gim_alloc(sizeof(GUINT)*aabbset->m_count );
aabbset->m_sorted_mincoords = (GIM_RSORT_TOKEN *)gim_alloc(sizeof(GIM_RSORT_TOKEN)*aabbset->m_count);
}
aabbset->m_shared = 0;
INVALIDATE_AABB(aabbset->m_global_bound);
}
//! Destroys the aabb set.
void gim_aabbset_destroy(GIM_AABB_SET * aabbset)
{
aabbset->m_count = 0;
if(aabbset->m_shared==0)
{
gim_free(aabbset->m_boxes,0);
gim_free(aabbset->m_maxcoords,0);
gim_free(aabbset->m_sorted_mincoords,0);
}
aabbset->m_boxes = 0;
aabbset->m_sorted_mincoords = 0;
aabbset->m_maxcoords = 0;
}
void gim_aabbset_calc_global_bound(GIM_AABB_SET * aabbset)
{
aabb3f * paabb = aabbset->m_boxes;
aabb3f * globalbox = &aabbset->m_global_bound;
AABB_COPY((*globalbox),(*paabb));
GUINT count = aabbset->m_count-1;
paabb++;
while(count)
{
MERGEBOXES(*globalbox,*paabb)
paabb++;
count--;
}
}
//! Sorts the boxes for box prunning.
/*!
1) find the integer representation of the aabb coords
2) Sorts the min coords
3) Calcs the global bound
\pre aabbset must be allocated. And the boxes must be already set.
\param aabbset
\param calc_global_bound If 1 , calcs the global bound
\post If aabbset->m_sorted_mincoords == 0, then it allocs the sorted coordinates
*/
void gim_aabbset_sort(GIM_AABB_SET * aabbset, char calc_global_bound)
{
if(aabbset->m_sorted_mincoords == 0)
{//allocate
aabbset->m_maxcoords = (GUINT *)gim_alloc(sizeof(GUINT)*aabbset->m_count );
aabbset->m_sorted_mincoords = (GIM_RSORT_TOKEN *)gim_alloc(sizeof(GIM_RSORT_TOKEN)*aabbset->m_count);
}
GUINT i, count = aabbset->m_count;
aabb3f * paabb = aabbset->m_boxes;
GUINT * maxcoords = aabbset->m_maxcoords;
GIM_RSORT_TOKEN * sorted_tokens = aabbset->m_sorted_mincoords;
if(count<860)//Calibrated on a Pentium IV
{
//Sort by quick sort
//Calculate keys
for(i=0;i<count;i++)
{
GIM_CONVERT_VEC3F_GUINT_XZ_UPPER(paabb[i].maxX,paabb[i].maxZ,maxcoords[i]);
GIM_CONVERT_VEC3F_GUINT_XZ(paabb[i].minX,paabb[i].minZ,sorted_tokens[i].m_key);
sorted_tokens[i].m_value = i;
}
GIM_QUICK_SORT_ARRAY(GIM_RSORT_TOKEN , sorted_tokens, count, RSORT_TOKEN_COMPARATOR,GIM_DEF_EXCHANGE_MACRO);
}
else
{
//Sort by radix sort
GIM_RSORT_TOKEN * unsorted = (GIM_RSORT_TOKEN *)gim_alloc(sizeof(GIM_RSORT_TOKEN )*count);
//Calculate keys
for(i=0;i<count;i++)
{
GIM_CONVERT_VEC3F_GUINT_XZ_UPPER(paabb[i].maxX,paabb[i].maxZ,maxcoords[i]);
GIM_CONVERT_VEC3F_GUINT_XZ(paabb[i].minX,paabb[i].minZ,unsorted[i].m_key);
unsorted[i].m_value = i;
}
GIM_RADIX_SORT_RTOKENS(unsorted,sorted_tokens,count);
gim_free(unsorted,0);
}
if(calc_global_bound) gim_aabbset_calc_global_bound(aabbset);
}
//utility macros
/*#define PUSH_PAIR(i,j,pairset)\
{\
GIM_PAIR _pair={i,j};\
GIM_DYNARRAY_PUSH_ITEM(GIM_PAIR,pairset,_pair);\
}*/
#define PUSH_PAIR(i,j,pairset)\
{\
GIM_DYNARRAY_PUSH_EMPTY(GIM_PAIR,pairset);\
GIM_PAIR * _pair = GIM_DYNARRAY_POINTER(GIM_PAIR,pairset) + (pairset).m_size - 1;\
_pair->m_index1 = i;\
_pair->m_index2 = j;\
}
#define PUSH_PAIR_INV(i,j,pairset)\
{\
GIM_DYNARRAY_PUSH_EMPTY(GIM_PAIR,pairset);\
GIM_PAIR * _pair = GIM_DYNARRAY_POINTER(GIM_PAIR,pairset) + (pairset).m_size - 1;\
_pair->m_index1 = j;\
_pair->m_index2 = i;\
}
#define FIND_OVERLAPPING_FOWARD(\
curr_index,\
test_count,\
test_aabb,\
max_coord_uint,\
sorted_tokens,\
aabbarray,\
pairset,\
push_pair_macro)\
{\
GUINT _i = test_count;\
char _intersected;\
GIM_RSORT_TOKEN * _psorted_tokens = sorted_tokens;\
while(max_coord_uint >= _psorted_tokens->m_key && _i>0)\
{\
AABBCOLLISION(_intersected,test_aabb,aabbarray[_psorted_tokens->m_value]);\
if(_intersected)\
{\
push_pair_macro(curr_index, _psorted_tokens->m_value,pairset);\
}\
_psorted_tokens++;\
_i--;\
}\
}
//! log(N) Complete box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to the same set.
/*!
\pre aabbset must be allocated and sorted, the boxes must be already set.
\param aabbset Must be sorted. Global bound isn't required
\param collision_pairs Array of GIM_PAIR elements. Must be initialized before (Reserve size ~ 100)
*/
void gim_aabbset_self_intersections_sorted(GIM_AABB_SET * aabbset, GDYNAMIC_ARRAY * collision_pairs)
{
collision_pairs->m_size = 0;
GUINT count = aabbset->m_count;
aabb3f * paabb = aabbset->m_boxes;
GUINT * maxcoords = aabbset->m_maxcoords;
GIM_RSORT_TOKEN * sorted_tokens = aabbset->m_sorted_mincoords;
aabb3f test_aabb;
while(count>1)
{
///current cache variables
GUINT curr_index = sorted_tokens->m_value;
GUINT max_coord_uint = maxcoords[curr_index];
AABB_COPY(test_aabb,paabb[curr_index]);
///next pairs
sorted_tokens++;
count--;
FIND_OVERLAPPING_FOWARD( curr_index, count, test_aabb, max_coord_uint, sorted_tokens , paabb, (*collision_pairs),PUSH_PAIR);
}
}
//! NxN Complete box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to the same set.
/*!
\pre aabbset must be allocated, the boxes must be already set.
\param aabbset Global bound isn't required. Doen't need to be sorted.
\param collision_pairs Array of GIM_PAIR elements. Must be initialized before (Reserve size ~ 100)
*/
void gim_aabbset_self_intersections_brute_force(GIM_AABB_SET * aabbset, GDYNAMIC_ARRAY * collision_pairs)
{
collision_pairs->m_size = 0;
GUINT i,j;
GUINT count = aabbset->m_count;
aabb3f * paabb = aabbset->m_boxes;
char intersected;
for (i=0;i< count-1 ;i++ )
{
for (j=i+1;j<count ;j++ )
{
AABBCOLLISION(intersected,paabb[i],paabb[j]);
if(intersected)
{
PUSH_PAIR(i,j,(*collision_pairs));
}
}
}
}
//! log(N) Bipartite box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to a different set.
/*!
\pre aabbset1 and aabbset2 must be allocated and sorted, the boxes must be already set.
\param aabbset1 Must be sorted, Global bound is required.
\param aabbset2 Must be sorted, Global bound is required.
\param collision_pairs Array of GIM_PAIR elements. Must be initialized before (Reserve size ~ 100)
*/
void gim_aabbset_bipartite_intersections_sorted(GIM_AABB_SET * aabbset1, GIM_AABB_SET * aabbset2, GDYNAMIC_ARRAY * collision_pairs)
{
char intersected;
collision_pairs->m_size = 0;
AABBCOLLISION(intersected,aabbset1->m_global_bound,aabbset2->m_global_bound);
if(intersected == 0) return;
GUINT count1 = aabbset1->m_count;
aabb3f * paabb1 = aabbset1->m_boxes;
GUINT * maxcoords1 = aabbset1->m_maxcoords;
GIM_RSORT_TOKEN * sorted_tokens1 = aabbset1->m_sorted_mincoords;
GUINT count2 = aabbset2->m_count;
aabb3f * paabb2 = aabbset2->m_boxes;
GUINT * maxcoords2 = aabbset2->m_maxcoords;
GIM_RSORT_TOKEN * sorted_tokens2 = aabbset2->m_sorted_mincoords;
GUINT curr_index;
GUINT max_coord_uint;
aabb3f test_aabb;
//Classify boxes
//Find Set intersection
aabb3f int_abbb;
BOXINTERSECTION(aabbset1->m_global_bound,aabbset2->m_global_bound, int_abbb);
//Clasify set 1
GIM_RSORT_TOKEN * classified_tokens1 = (GIM_RSORT_TOKEN *) gim_alloc(sizeof(GIM_RSORT_TOKEN)*count1);
GUINT i,classified_count1 = 0,classified_count2 = 0;
for (i=0;i<count1;i++ )
{
curr_index = sorted_tokens1[i].m_value;
AABBCOLLISION(intersected,paabb1[curr_index],int_abbb);
if(intersected)
{
classified_tokens1[classified_count1] = sorted_tokens1[i];
classified_count1++;
}
}
if(classified_count1==0)
{
gim_free(classified_tokens1 ,0);
return; // no pairs
}
//Clasify set 2
GIM_RSORT_TOKEN * classified_tokens2 = (GIM_RSORT_TOKEN *) gim_alloc(sizeof(GIM_RSORT_TOKEN)*count2);
for (i=0;i<count2;i++ )
{
curr_index = sorted_tokens2[i].m_value;
AABBCOLLISION(intersected,paabb2[curr_index],int_abbb);
if(intersected)
{
classified_tokens2[classified_count2] = sorted_tokens2[i];
classified_count2++;
}
}
if(classified_count2==0)
{
gim_free(classified_tokens1 ,0);
gim_free(classified_tokens2 ,0);
return; // no pairs
}
sorted_tokens1 = classified_tokens1;
sorted_tokens2 = classified_tokens2;
while(classified_count1>0&&classified_count2>0)
{
if(sorted_tokens1->m_key <= sorted_tokens2->m_key)
{
///current cache variables
curr_index = sorted_tokens1->m_value;
max_coord_uint = maxcoords1[curr_index];
AABB_COPY(test_aabb,paabb1[curr_index]);
///next pairs
sorted_tokens1++;
classified_count1--;
FIND_OVERLAPPING_FOWARD( curr_index, classified_count2, test_aabb, max_coord_uint, sorted_tokens2 , paabb2, (*collision_pairs), PUSH_PAIR);
}
else ///Switch test
{
///current cache variables
curr_index = sorted_tokens2->m_value;
max_coord_uint = maxcoords2[curr_index];
AABB_COPY(test_aabb,paabb2[curr_index]);
///next pairs
sorted_tokens2++;
classified_count2--;
FIND_OVERLAPPING_FOWARD( curr_index, classified_count1, test_aabb, max_coord_uint, sorted_tokens1 , paabb1, (*collision_pairs), PUSH_PAIR_INV );
}
}
gim_free(classified_tokens1 ,0);
gim_free(classified_tokens2 ,0);
}
//! NxM Bipartite box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to a different set.
/*!
\pre aabbset1 and aabbset2 must be allocated and sorted, the boxes must be already set.
\param aabbset1 Must be sorted, Global bound is required.
\param aabbset2 Must be sorted, Global bound is required.
\param collision_pairs Array of GIM_PAIR elements. Must be initialized before (Reserve size ~ 100)
*/
void gim_aabbset_bipartite_intersections_brute_force(GIM_AABB_SET * aabbset1,GIM_AABB_SET * aabbset2, GDYNAMIC_ARRAY * collision_pairs)
{
char intersected;
collision_pairs->m_size = 0;
AABBCOLLISION(intersected,aabbset1->m_global_bound,aabbset2->m_global_bound);
if(intersected == 0) return;
aabb3f int_abbb;
//Find Set intersection
BOXINTERSECTION(aabbset1->m_global_bound,aabbset2->m_global_bound, int_abbb);
//Clasify set 1
GUINT i,j;
GUINT classified_count = 0;
GUINT count = aabbset1->m_count;
aabb3f * paabb1 = aabbset1->m_boxes;
aabb3f * paabb2 = aabbset2->m_boxes;
GUINT * classified = (GUINT *) gim_alloc(sizeof(GUINT)*count);
for (i=0;i<count;i++ )
{
AABBCOLLISION(intersected,paabb1[i],int_abbb);
if(intersected)
{
classified[classified_count] = i;
classified_count++;
}
}
if(classified_count==0) return; // no pairs
//intesect set2
count = aabbset2->m_count;
for (i=0;i<count;i++)
{
AABBCOLLISION(intersected,paabb2[i],int_abbb);
if(intersected)
{
for (j=0;j<classified_count;j++)
{
AABBCOLLISION(intersected,paabb2[i],paabb1[classified[j]]);
if(intersected)
{
PUSH_PAIR(classified[j],i,(*collision_pairs));
}
}
}
}
gim_free(classified,0);
}
//! Initalizes the set. Sort Boxes if needed.
/*!
\pre aabbset must be allocated. And the boxes must be already set.
\post If the set has less of GIM_MIN_SORTED_BIPARTITE_PRUNING_BOXES boxes, only calcs the global box,
else it Sorts the entire set( Only applicable for large sets)
*/
void gim_aabbset_update(GIM_AABB_SET * aabbset)
{
if(aabbset->m_count < GIM_MIN_SORTED_BIPARTITE_PRUNING_BOXES)
{//Brute force approach
gim_aabbset_calc_global_bound(aabbset);
}
else
{//Sorted force approach
gim_aabbset_sort(aabbset,1);
}
}
//! Complete box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to the same set.
/*!
This function sorts the set and then it calls to gim_aabbset_self_intersections_brute_force or gim_aabbset_self_intersections_sorted.
\param aabbset Set of boxes. Sorting isn't required.
\param collision_pairs Array of GIM_PAIR elements. Must be initialized before (Reserve size ~ 100)
\pre aabbset must be allocated and initialized.
\post If aabbset->m_count >= GIM_MIN_SORTED_PRUNING_BOXES, then it calls to gim_aabbset_sort and then to gim_aabbset_self_intersections_sorted.
*/
void gim_aabbset_self_intersections(GIM_AABB_SET * aabbset, GDYNAMIC_ARRAY * collision_pairs)
{
if(aabbset->m_count < GIM_MIN_SORTED_PRUNING_BOXES)
{//Brute force approach
gim_aabbset_self_intersections_brute_force(aabbset,collision_pairs);
}
else
{//Sorted force approach
gim_aabbset_sort(aabbset,0);
gim_aabbset_self_intersections_sorted(aabbset,collision_pairs);
}
}
//! Collides two sets. Returns a list of overlapping pairs of boxes, each box of the pair belongs to a different set.
/*!
\pre aabbset1 and aabbset2 must be allocated and updated. See .
\param aabbset1 Must be sorted, Global bound is required.
\param aabbset2 Must be sorted, Global bound is required.
\param collision_pairs Array of GIM_PAIR elements. Must be initialized before (Reserve size ~ 100)
*/
void gim_aabbset_bipartite_intersections(GIM_AABB_SET * aabbset1, GIM_AABB_SET * aabbset2, GDYNAMIC_ARRAY * collision_pairs)
{
if(aabbset1->m_sorted_mincoords == 0||aabbset2->m_sorted_mincoords == 0)
{//Brute force approach
gim_aabbset_bipartite_intersections_brute_force(aabbset1,aabbset2,collision_pairs);
}
else
{//Sorted force approach
gim_aabbset_bipartite_intersections_sorted(aabbset1,aabbset2,collision_pairs);
}
}
void gim_aabbset_box_collision(aabb3f *test_aabb, GIM_AABB_SET * aabbset, GDYNAMIC_ARRAY * collided)
{
collided->m_size = 0;
char intersected;
AABBCOLLISION(intersected,aabbset->m_global_bound,(*test_aabb));
if(intersected == 0) return;
GUINT i;
GUINT count = aabbset->m_count;
aabb3f * paabb = aabbset->m_boxes;
aabb3f _testaabb;
AABB_COPY(_testaabb,*test_aabb);
for (i=0;i< count;i++ )
{
AABBCOLLISION(intersected,paabb[i],_testaabb);
if(intersected)
{
GIM_DYNARRAY_PUSH_ITEM(GUINT,(*collided),i);
}
}
}
void gim_aabbset_ray_collision(vec3f vorigin,vec3f vdir, GREAL tmax, GIM_AABB_SET * aabbset, GDYNAMIC_ARRAY * collided)
{
collided->m_size = 0;
char intersected;
GREAL tparam = 0;
BOX_INTERSECTS_RAY(aabbset->m_global_bound, vorigin, vdir, tparam, tmax,intersected);
if(intersected==0) return;
GUINT i;
GUINT count = aabbset->m_count;
aabb3f * paabb = aabbset->m_boxes;
for (i=0;i< count;i++ )
{
BOX_INTERSECTS_RAY(paabb[i], vorigin, vdir, tparam, tmax,intersected);
if(intersected)
{
GIM_DYNARRAY_PUSH_ITEM(GUINT,(*collided),i);
}
}
}

View File

@ -0,0 +1,132 @@
/*
-----------------------------------------------------------------------------
This source file is part of GIMPACT Library.
For the latest info, see http://gimpact.sourceforge.net/
Copyright (c) 2006 Francisco Leon. 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.
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 and GIMPACT-LICENSE-BSD.TXT for more details.
-----------------------------------------------------------------------------
*/
#include "GIMPACT/gim_contact.h"
void gim_merge_contacts(GDYNAMIC_ARRAY * source_contacts,
GDYNAMIC_ARRAY * dest_contacts)
{
dest_contacts->m_size = 0;
GUINT source_count = source_contacts->m_size;
GIM_CONTACT * psource_contacts = GIM_DYNARRAY_POINTER(GIM_CONTACT,(*source_contacts));
//create keys
GIM_RSORT_TOKEN * keycontacts = (GIM_RSORT_TOKEN * )gim_alloc(sizeof(GIM_RSORT_TOKEN)*source_count);
GUINT i;
for(i=0;i<source_count;i++)
{
keycontacts[i].m_value = i;
GIM_CALC_KEY_CONTACT(psource_contacts[i].m_point,keycontacts[i].m_key);
}
//sort keys
GIM_QUICK_SORT_ARRAY(GIM_RSORT_TOKEN , keycontacts, source_count, RSORT_TOKEN_COMPARATOR,GIM_DEF_EXCHANGE_MACRO);
// Merge contacts
GIM_CONTACT * pcontact = 0;
GIM_CONTACT * scontact = 0;
GUINT key,last_key=0;
for(i=0;i<source_contacts->m_size;i++)
{
key = keycontacts[i].m_key;
scontact = &psource_contacts[keycontacts[i].m_value];
if(i>0 && last_key == key)
{
//merge contact
if(pcontact->m_depth > scontact->m_depth + CONTACT_DIFF_EPSILON)
{
GIM_COPY_CONTACTS(pcontact, scontact);
}
}
else
{//add new contact
GIM_DYNARRAY_PUSH_EMPTY(GIM_CONTACT,(*dest_contacts));
pcontact = GIM_DYNARRAY_POINTER_LAST(GIM_CONTACT,(*dest_contacts));
GIM_COPY_CONTACTS(pcontact, scontact);
}
last_key = key;
}
gim_free(keycontacts,0);
}
void gim_merge_contacts_unique(GDYNAMIC_ARRAY * source_contacts,
GDYNAMIC_ARRAY * dest_contacts)
{
dest_contacts->m_size = 0;
//Traverse the source contacts
GUINT source_count = source_contacts->m_size;
if(source_count==0) return;
GIM_CONTACT * psource_contacts = GIM_DYNARRAY_POINTER(GIM_CONTACT,(*source_contacts));
//add the unique contact
GIM_CONTACT * pcontact = 0;
GIM_DYNARRAY_PUSH_EMPTY(GIM_CONTACT,(*dest_contacts));
pcontact = GIM_DYNARRAY_POINTER_LAST(GIM_CONTACT,(*dest_contacts));
//set the first contact
GIM_COPY_CONTACTS(pcontact, psource_contacts);
if(source_count==1) return;
//scale the first contact
VEC_SCALE(pcontact->m_normal,pcontact->m_depth,pcontact->m_normal);
psource_contacts++;
//Average the contacts
GUINT i;
for(i=1;i<source_count;i++)
{
VEC_SUM(pcontact->m_point,pcontact->m_point,psource_contacts->m_point);
VEC_ACCUM(pcontact->m_normal,psource_contacts->m_depth,psource_contacts->m_normal);
psource_contacts++;
}
GREAL divide_average = 1.0f/((GREAL)source_count);
VEC_SCALE(pcontact->m_point,divide_average,pcontact->m_point);
pcontact->m_depth = VEC_DOT(pcontact->m_normal,pcontact->m_normal)*divide_average;
GIM_SQRT(pcontact->m_depth,pcontact->m_depth);
VEC_NORMALIZE(pcontact->m_normal);
/*GREAL normal_len;
VEC_INV_LENGTH(pcontact->m_normal,normal_len);
VEC_SCALE(pcontact->m_normal,normal_len,pcontact->m_normal);
//Deep = LEN(normal)/SQRT(source_count)
GIM_SQRT(divide_average,divide_average);
pcontact->m_depth = divide_average/normal_len;
*/
}

View File

@ -0,0 +1,60 @@
/*
-----------------------------------------------------------------------------
This source file is part of GIMPACT Library.
For the latest info, see http://gimpact.sourceforge.net/
Copyright (c) 2006 Francisco Leon. 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.
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 and GIMPACT-LICENSE-BSD.TXT for more details.
-----------------------------------------------------------------------------
*/
#include "GIMPACT/gim_math.h"
#include "stdlib.h"
#include "time.h"
GREAL gim_inv_sqrt(GREAL f)
{
GREAL r;
GIM_INV_SQRT(f,r);
return r;
}
GREAL gim_sqrt(GREAL f)
{
GREAL r;
GIM_SQRT(f,r);
return r;
}
//!Initializes mathematical functions
void gim_init_math()
{
srand( static_cast< unsigned int >( time( 0 ) ) );
}
//! Generates an unit random
GREAL gim_unit_random()
{
GREAL rn = static_cast< GREAL >( rand() );
rn/=(GREAL)RAND_MAX;
return rn;
}

View File

@ -0,0 +1,848 @@
/*
-----------------------------------------------------------------------------
This source file is part of GIMPACT Library.
For the latest info, see http://gimpact.sourceforge.net/
Copyright (c) 2006 Francisco Leon. 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.
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 and GIMPACT-LICENSE-BSD.TXT for more details.
-----------------------------------------------------------------------------
*/
#include "GIMPACT/gim_memory.h"
#include "stdlib.h"
#include "malloc.h"
//#include "mm_malloc.h"
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;
// buffer managers
#define MAX_BUFFER_MANAGERS 16
static GBUFFER_MANAGER_DATA g_buffer_managers[MAX_BUFFER_MANAGERS];
static GUINT g_buffer_managers_count = 0;
#define VALIDATE_BUFFER_MANAGER(buffer_manager_id)\
if(buffer_manager_id>=MAX_BUFFER_MANAGERS) return G_BUFFER_OP_INVALID;\
GBUFFER_MANAGER_DATA * bm_data;\
gim_get_buffer_manager_data(buffer_manager_id,&bm_data);\
if(bm_data == 0) return G_BUFFER_OP_INVALID;\
#define VALIDATE_BUFFER_ID_PT(buffer_id)\
VALIDATE_BUFFER_MANAGER(buffer_id->m_buffer_manager_id)\
if(buffer_id->m_buffer_id>=bm_data->m_buffer_array.m_size) return G_BUFFER_OP_INVALID;\
GBUFFER_DATA * pbuffer = GIM_DYNARRAY_POINTER(GBUFFER_DATA,bm_data->m_buffer_array);\
pbuffer += buffer_id->m_buffer_id;\
if(pbuffer->m_buffer_handle==0) return G_BUFFER_OP_INVALID;\
void GIM_BUFFER_ARRAY_DESTROY(GBUFFER_ARRAY & array_data)
{
gim_buffer_array_unlock(&array_data);
gim_buffer_free(&(array_data).m_buffer_id);
}
void GIM_DYNARRAY_DESTROY(GDYNAMIC_ARRAY & array_data)
{
if(array_data.m_pdata != 0)
{
gim_free(array_data.m_pdata,0);
array_data.m_reserve_size = 0;
array_data.m_size = 0;
array_data.m_pdata = 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 = 0;
ptr = malloc(size);
/*if (g_allocfn) ptr = g_allocfn(size); else ptr = malloc(size);//_mm_malloc(size,0);*/
if(ptr==0)
{
float * fp = 0;
*fp = 0.0f;
}
return ptr;
}
void * gim_alloca(size_t size)
{
if (g_allocafn) return g_allocafn(size); else return alloca(size);
}
void * gim_realloc(void *ptr, size_t oldsize, size_t newsize)
{
/*if (g_reallocfn) return g_reallocfn(ptr,oldsize,newsize);
else return realloc(ptr,newsize);*/
//return realloc(ptr,newsize);
void * newptr = gim_alloc(newsize);
size_t copysize = newsize> oldsize? oldsize: newsize;
memcpy(newptr,ptr,copysize);
gim_free(ptr,oldsize);
return newptr;
}
void gim_free(void *ptr, size_t size)
{
if (!ptr) return;
if (g_freefn)
{
g_freefn(ptr,size);
}
else
{
free(ptr);//_mm_free(ptr);
}
}
///******************************* BUFFER MANAGERS ******************************///
//!** Basic buffer prototyoe functions
GUINT _system_buffer_alloc_function(GUINT size,int usage)
{
void * newdata = gim_alloc(size);
memset(newdata,0,size);
return (GUINT)(newdata);
}
GUINT _system_buffer_alloc_data_function(const void * pdata,GUINT size,int usage)
{
void * newdata = gim_alloc(size);
memcpy(newdata,pdata,size);
return (GUINT)(newdata);
}
GUINT _system_buffer_realloc_function(GUINT buffer_handle,GUINT oldsize,int old_usage,GUINT newsize,int new_usage)
{
void * newdata = gim_realloc((void *)buffer_handle,oldsize,newsize);
return (GUINT)(newdata);
}
void _system_buffer_free_function(GUINT buffer_handle,GUINT size)
{
gim_free((void*)buffer_handle,size);
}
char * _system_lock_buffer_function(GUINT buffer_handle,int access)
{
return (char * )(buffer_handle);
}
void _system_unlock_buffer_function(GUINT buffer_handle)
{
}
void _system_download_from_buffer_function(
GUINT source_buffer_handle,
GUINT source_pos,
void * destdata,
GUINT copysize)
{
char * pdata;
pdata = (char *)source_buffer_handle;
memcpy(destdata,pdata+source_pos,copysize);
}
void _system_upload_to_buffer_function(
GUINT dest_buffer_handle,
GUINT dest_pos,
void * sourcedata,
GUINT copysize)
{
char * pdata;
pdata = (char * )dest_buffer_handle;
memcpy(pdata+dest_pos,sourcedata,copysize);
}
void _system_copy_buffers_function(
GUINT source_buffer_handle,
GUINT source_pos,
GUINT dest_buffer_handle,
GUINT dest_pos,
GUINT copysize)
{
char * pdata1,*pdata2;
pdata1 = (char *)source_buffer_handle;
pdata2 = (char *)dest_buffer_handle;
memcpy(pdata2+dest_pos,pdata1+source_pos,copysize);
}
GUINT _shared_buffer_alloc_function(GUINT size,int usage)
{
return 0;
}
GUINT _shared_buffer_alloc_data_function(const void * pdata,GUINT size,int usage)
{
return (GUINT)pdata;
}
GUINT _shared_buffer_realloc_function(GUINT buffer_handle,GUINT oldsize,int old_usage,GUINT newsize,int new_usage)
{
return 0;
}
void _shared_buffer_free_function(GUINT buffer_handle,GUINT size)
{
}
//!** Buffer manager operations
void gim_create_buffer_manager(GBUFFER_MANAGER_PROTOTYPE * prototype,GUINT buffer_manager_id)
{
GBUFFER_MANAGER_DATA * bm_data;
bm_data = &g_buffer_managers[buffer_manager_id];
if(bm_data->m_active==0)
{
if(g_buffer_managers_count<=buffer_manager_id)
{
g_buffer_managers_count = buffer_manager_id+1;
}
}
else
{
gim_destroy_buffer_manager(buffer_manager_id);
}
bm_data->m_active = 1;
//CREATE ARRAYS
GIM_DYNARRAY_CREATE(GBUFFER_DATA,bm_data->m_buffer_array,G_ARRAY_GROW_SIZE);
GIM_DYNARRAY_CREATE(GUINT,bm_data->m_free_positions,G_ARRAY_GROW_SIZE);
//INIT PROTOTYPE
bm_data->m_prototype.alloc_data_fn = prototype->alloc_data_fn;
bm_data->m_prototype.alloc_fn = prototype->alloc_fn;
bm_data->m_prototype.copy_buffers_fn = prototype->copy_buffers_fn;
bm_data->m_prototype.download_from_buffer_fn = prototype->download_from_buffer_fn;
bm_data->m_prototype.free_fn = prototype->free_fn;
bm_data->m_prototype.lock_buffer_fn = prototype->lock_buffer_fn;
bm_data->m_prototype.realloc_fn = prototype->realloc_fn;
bm_data->m_prototype.unlock_buffer_fn = prototype->unlock_buffer_fn;
bm_data->m_prototype.upload_to_buffer_fn = prototype->upload_to_buffer_fn;
}
GUINT gim_get_buffer_manager_count()
{
return g_buffer_managers_count;
}
void gim_destroy_buffer_manager(GUINT buffer_manager_id)
{
GBUFFER_MANAGER_DATA * bm_data;
gim_get_buffer_manager_data(buffer_manager_id,&bm_data);
if(bm_data == 0) return;
//Destroy all buffers
GBUFFER_DATA * buffers = GIM_DYNARRAY_POINTER(GBUFFER_DATA,bm_data->m_buffer_array);
GUINT i, buffer_count = bm_data->m_buffer_array.m_size;
for (i=0;i<buffer_count ;i++ )
{
if(buffers[i].m_buffer_handle!=0) //Is active
{
// free handle
bm_data->m_prototype.free_fn(buffers[i].m_buffer_handle,buffers[i].m_size);
}
}
//destroy buffer array
GIM_DYNARRAY_DESTROY(bm_data->m_buffer_array);
//destroy free positions
GIM_DYNARRAY_DESTROY(bm_data->m_free_positions);
//Mark as innactive
bm_data->m_active = 0;
}
void gim_get_buffer_manager_data(GUINT buffer_manager_id,GBUFFER_MANAGER_DATA ** pbm_data)
{
GBUFFER_MANAGER_DATA * bm_data;
bm_data = &g_buffer_managers[buffer_manager_id];
if(bm_data->m_active==0)
{
*pbm_data = 0;
}
else
{
*pbm_data = bm_data;
}
}
void gim_init_buffer_managers()
{
GUINT i;
for (i=0;i<MAX_BUFFER_MANAGERS;i++)
{
g_buffer_managers[i].m_active = 0;
g_buffer_managers[i].m_buffer_array.m_pdata = 0;
g_buffer_managers[i].m_buffer_array.m_reserve_size = 0;
g_buffer_managers[i].m_buffer_array.m_size = 0;
g_buffer_managers[i].m_free_positions.m_pdata = 0;
g_buffer_managers[i].m_free_positions.m_reserve_size = 0;
g_buffer_managers[i].m_free_positions.m_size = 0;
}
g_buffer_managers_count = 0;
// Add the two most important buffer managers
GBUFFER_MANAGER_PROTOTYPE prototype;
//add system buffer manager
prototype.alloc_data_fn = _system_buffer_alloc_data_function;
prototype.alloc_fn = _system_buffer_alloc_function;
prototype.copy_buffers_fn = _system_copy_buffers_function;
prototype.download_from_buffer_fn = _system_download_from_buffer_function;
prototype.free_fn = _system_buffer_free_function;
prototype.lock_buffer_fn = _system_lock_buffer_function;
prototype.realloc_fn = _system_buffer_realloc_function;
prototype.unlock_buffer_fn = _system_unlock_buffer_function;
prototype.upload_to_buffer_fn = _system_upload_to_buffer_function;
gim_create_buffer_manager(&prototype,G_BUFFER_MANAGER_SYSTEM );
//add zhared buffer manager
prototype.alloc_data_fn = _shared_buffer_alloc_data_function;
prototype.alloc_fn = _shared_buffer_alloc_function;
prototype.free_fn = _shared_buffer_free_function;
gim_create_buffer_manager(&prototype,G_BUFFER_MANAGER_SHARED);
}
void gim_terminate_buffer_managers()
{
GUINT i;
for (i=0;i<g_buffer_managers_count;i++)
{
gim_destroy_buffer_manager(i);
}
g_buffer_managers_count = 0;
}
//!** Nuffer operations
void GET_AVALIABLE_BUFFER_ID(GBUFFER_MANAGER_DATA * buffer_manager, GUINT & buffer_id)
{
if(buffer_manager->m_free_positions.m_size>0)\
{
GUINT * _pointer = GIM_DYNARRAY_POINTER(GUINT,buffer_manager->m_free_positions);
buffer_id = _pointer[buffer_manager->m_free_positions.m_size-1];
GIM_DYNARRAY_POP_ITEM(buffer_manager->m_free_positions);
}
else
{
buffer_id = buffer_manager->m_buffer_array.m_size;
GIM_DYNARRAY_PUSH_EMPTY(GBUFFER_DATA,buffer_manager->m_buffer_array);
}
}
GINT _validate_buffer_id(GBUFFER_ID * buffer_id,GBUFFER_DATA ** ppbuffer,GBUFFER_MANAGER_DATA ** pbm_data)
{
VALIDATE_BUFFER_ID_PT(buffer_id)
*ppbuffer = pbuffer;
*pbm_data = bm_data;
return G_BUFFER_OP_SUCCESS;
}
GUINT gim_create_buffer(
GUINT buffer_manager_id,
GUINT buffer_size,
int usage,
GBUFFER_ID * buffer_id)
{
VALIDATE_BUFFER_MANAGER(buffer_manager_id)
GUINT newbufferhandle = bm_data->m_prototype.alloc_fn(buffer_size,usage);
if(newbufferhandle==0) return G_BUFFER_OP_INVALID;
GET_AVALIABLE_BUFFER_ID(bm_data,buffer_id->m_buffer_id);
buffer_id->m_buffer_manager_id = buffer_manager_id;
GBUFFER_DATA * pbuffer = GIM_DYNARRAY_POINTER(GBUFFER_DATA,bm_data->m_buffer_array);
pbuffer += buffer_id->m_buffer_id ;
pbuffer->m_buffer_handle = newbufferhandle;
pbuffer->m_size = buffer_size;
pbuffer->m_usage = usage;
pbuffer->m_lock_count = 0;
pbuffer->m_refcount = 0;
pbuffer->m_mapped_pointer = 0;
//set shadow buffer if needed
if(usage == G_MU_STATIC_READ ||
usage == G_MU_STATIC_READ_DYNAMIC_WRITE||
usage == G_MU_STATIC_READ_DYNAMIC_WRITE_COPY)
{
gim_create_common_buffer(buffer_size,&pbuffer->m_shadow_buffer);
}
else
{
pbuffer->m_shadow_buffer.m_buffer_id = G_UINT_INFINITY;
pbuffer->m_shadow_buffer.m_buffer_manager_id = G_UINT_INFINITY;
}
return G_BUFFER_OP_SUCCESS;
}
GUINT gim_create_buffer_from_data(
GUINT buffer_manager_id,
const void * pdata,
GUINT buffer_size,
int usage,
GBUFFER_ID * buffer_id)
{
VALIDATE_BUFFER_MANAGER(buffer_manager_id)
GUINT newbufferhandle = bm_data->m_prototype.alloc_data_fn(pdata,buffer_size,usage);
if(newbufferhandle==0) return G_BUFFER_OP_INVALID;
GET_AVALIABLE_BUFFER_ID(bm_data,buffer_id->m_buffer_id);
buffer_id->m_buffer_manager_id = buffer_manager_id;
GBUFFER_DATA * pbuffer = GIM_DYNARRAY_POINTER(GBUFFER_DATA,bm_data->m_buffer_array);
pbuffer += buffer_id->m_buffer_id ;
pbuffer->m_buffer_handle = newbufferhandle;
pbuffer->m_size = buffer_size;
pbuffer->m_usage = usage;
pbuffer->m_lock_count = 0;
pbuffer->m_mapped_pointer = 0;
pbuffer->m_refcount = 0;
//set shadow buffer if needed
if(usage == G_MU_STATIC_READ ||
usage == G_MU_STATIC_READ_DYNAMIC_WRITE||
usage == G_MU_STATIC_READ_DYNAMIC_WRITE_COPY)
{
gim_create_common_buffer_from_data(pdata,buffer_size,&pbuffer->m_shadow_buffer);
}
else
{
pbuffer->m_shadow_buffer.m_buffer_id = G_UINT_INFINITY;
pbuffer->m_shadow_buffer.m_buffer_manager_id = G_UINT_INFINITY;
}
return G_BUFFER_OP_SUCCESS;
}
GUINT gim_create_common_buffer(GUINT buffer_size, GBUFFER_ID * buffer_id)
{
return gim_create_buffer(G_BUFFER_MANAGER_SYSTEM,buffer_size,G_MU_DYNAMIC_READ_WRITE,buffer_id);
}
GUINT gim_create_common_buffer_from_data(
const void * pdata, GUINT buffer_size, GBUFFER_ID * buffer_id)
{
return gim_create_buffer_from_data(G_BUFFER_MANAGER_SYSTEM,pdata,buffer_size,G_MU_DYNAMIC_READ_WRITE,buffer_id);
}
GUINT gim_create_shared_buffer_from_data(
const void * pdata, GUINT buffer_size, GBUFFER_ID * buffer_id)
{
return gim_create_buffer_from_data(G_BUFFER_MANAGER_SHARED,pdata,buffer_size,G_MU_DYNAMIC_READ_WRITE,buffer_id);
}
GINT gim_buffer_realloc(GBUFFER_ID * buffer_id,GUINT newsize)
{
VALIDATE_BUFFER_ID_PT(buffer_id)
if(pbuffer->m_lock_count>0) return G_BUFFER_OP_INVALID;
GUINT newhandle = bm_data->m_prototype.realloc_fn(pbuffer->m_buffer_handle,pbuffer->m_size,pbuffer->m_usage,newsize,pbuffer->m_usage);
if(newhandle==0) return G_BUFFER_OP_INVALID;
pbuffer->m_buffer_handle = newhandle;
//realloc shadow buffer if any
gim_buffer_realloc(&pbuffer->m_shadow_buffer,newsize);
return G_BUFFER_OP_SUCCESS;
}
GINT gim_buffer_add_ref(GBUFFER_ID * buffer_id)
{
VALIDATE_BUFFER_ID_PT(buffer_id)
pbuffer->m_refcount++;
return G_BUFFER_OP_SUCCESS;
}
GINT gim_buffer_free(GBUFFER_ID * buffer_id)
{
VALIDATE_BUFFER_ID_PT(buffer_id)
if(pbuffer->m_lock_count>0) return G_BUFFER_OP_INVALID;
if(pbuffer->m_refcount>0) pbuffer->m_refcount--;
if(pbuffer->m_refcount>0) return G_BUFFER_OP_STILLREFCOUNTED;
bm_data->m_prototype.free_fn(pbuffer->m_buffer_handle,pbuffer->m_size);
//destroy shadow buffer if needed
gim_buffer_free(&pbuffer->m_shadow_buffer);
// Obtain a free slot index for a new buffer
GIM_DYNARRAY_PUSH_ITEM(GUINT,bm_data->m_free_positions,buffer_id->m_buffer_id);
pbuffer->m_buffer_handle = 0;
pbuffer->m_size = 0;
pbuffer->m_shadow_buffer.m_buffer_id = G_UINT_INFINITY;
pbuffer->m_shadow_buffer.m_buffer_manager_id = G_UINT_INFINITY;
return G_BUFFER_OP_SUCCESS;
}
GINT gim_lock_buffer(GBUFFER_ID * buffer_id,int access,char ** map_pointer)
{
VALIDATE_BUFFER_ID_PT(buffer_id)
if(pbuffer->m_lock_count>0)
{
if(pbuffer->m_access!=access) return G_BUFFER_OP_INVALID;
pbuffer->m_lock_count++;
*map_pointer = pbuffer->m_mapped_pointer;
return G_BUFFER_OP_SUCCESS;
}
pbuffer->m_access = access;
GUINT result;
if(pbuffer->m_usage==G_MU_STATIC_WRITE)
{
*map_pointer = 0;///no access
return G_BUFFER_OP_INVALID;
}
else if(pbuffer->m_usage==G_MU_STATIC_READ)
{
if(pbuffer->m_access == G_MA_READ_ONLY)
{
result = gim_lock_buffer(&pbuffer->m_shadow_buffer,access,map_pointer);
if(result!= G_BUFFER_OP_SUCCESS) return G_BUFFER_OP_INVALID;
pbuffer->m_mapped_pointer = *map_pointer;
pbuffer->m_lock_count++;
}
else
{
*map_pointer = 0;
return G_BUFFER_OP_INVALID;
}
}
else if(pbuffer->m_usage==G_MU_STATIC_READ_DYNAMIC_WRITE)
{
if(pbuffer->m_access == G_MA_READ_ONLY)
{
result = gim_lock_buffer(&pbuffer->m_shadow_buffer,access,map_pointer);
if(result!= G_BUFFER_OP_SUCCESS) return G_BUFFER_OP_INVALID;
pbuffer->m_mapped_pointer = *map_pointer;
pbuffer->m_lock_count++;
}
else if(pbuffer->m_access == G_MA_WRITE_ONLY)
{
pbuffer->m_mapped_pointer = bm_data->m_prototype.lock_buffer_fn(pbuffer->m_buffer_handle,access);
*map_pointer = pbuffer->m_mapped_pointer;
pbuffer->m_lock_count++;
}
else if(pbuffer->m_access == G_MA_READ_WRITE)
{
*map_pointer = 0;
return G_BUFFER_OP_INVALID;
}
}
else if(pbuffer->m_usage==G_MU_STATIC_READ_DYNAMIC_WRITE_COPY)
{
result = gim_lock_buffer(&pbuffer->m_shadow_buffer,access,map_pointer);
if(result!= G_BUFFER_OP_SUCCESS) return G_BUFFER_OP_INVALID;
pbuffer->m_mapped_pointer = *map_pointer;
pbuffer->m_lock_count++;
}
else if(pbuffer->m_usage==G_MU_STATIC_WRITE_DYNAMIC_READ)
{
if(pbuffer->m_access == G_MA_READ_ONLY)
{
pbuffer->m_mapped_pointer = bm_data->m_prototype.lock_buffer_fn(pbuffer->m_buffer_handle,access);
*map_pointer = pbuffer->m_mapped_pointer;
pbuffer->m_lock_count++;
}
else
{
*map_pointer = 0;
return G_BUFFER_OP_INVALID;
}
}
else if(pbuffer->m_usage==G_MU_DYNAMIC_READ_WRITE)
{
pbuffer->m_mapped_pointer = bm_data->m_prototype.lock_buffer_fn(pbuffer->m_buffer_handle,access);
*map_pointer = pbuffer->m_mapped_pointer;
pbuffer->m_lock_count++;
}
return G_BUFFER_OP_SUCCESS;
}
GINT gim_unlock_buffer(GBUFFER_ID * buffer_id)
{
VALIDATE_BUFFER_ID_PT(buffer_id)
if(pbuffer->m_lock_count==0) return G_BUFFER_OP_INVALID;
if(pbuffer->m_lock_count>1)
{
pbuffer->m_lock_count--;
return G_BUFFER_OP_SUCCESS;
}
GUINT result;
if(pbuffer->m_usage==G_MU_STATIC_WRITE)
{
pbuffer->m_mapped_pointer = 0;
pbuffer->m_lock_count=0;
return G_BUFFER_OP_INVALID;
}
else if(pbuffer->m_usage==G_MU_STATIC_READ)
{
if(pbuffer->m_access == G_MA_READ_ONLY)
{
result = gim_unlock_buffer(&pbuffer->m_shadow_buffer);
if(result!= G_BUFFER_OP_SUCCESS) return G_BUFFER_OP_INVALID;
pbuffer->m_mapped_pointer = 0;
pbuffer->m_lock_count=0;
}
else
{
pbuffer->m_mapped_pointer = 0;
pbuffer->m_lock_count=0;
return G_BUFFER_OP_INVALID;
}
}
else if(pbuffer->m_usage==G_MU_STATIC_READ_DYNAMIC_WRITE)
{
if(pbuffer->m_access == G_MA_READ_ONLY)
{
result = gim_unlock_buffer(&pbuffer->m_shadow_buffer);
if(result!= G_BUFFER_OP_SUCCESS) return G_BUFFER_OP_INVALID;
pbuffer->m_mapped_pointer = 0;
pbuffer->m_lock_count=0;
}
else if(pbuffer->m_access == G_MA_WRITE_ONLY)
{
bm_data->m_prototype.unlock_buffer_fn(pbuffer->m_buffer_handle);
pbuffer->m_mapped_pointer = 0;
pbuffer->m_lock_count=0;
}
else if(pbuffer->m_access == G_MA_READ_WRITE)
{
pbuffer->m_mapped_pointer = 0;
pbuffer->m_lock_count=0;
return G_BUFFER_OP_INVALID;
}
}
else if(pbuffer->m_usage==G_MU_STATIC_READ_DYNAMIC_WRITE_COPY)
{
result = gim_unlock_buffer(&pbuffer->m_shadow_buffer);
if(result!= G_BUFFER_OP_SUCCESS) return G_BUFFER_OP_INVALID;
pbuffer->m_mapped_pointer = 0;
pbuffer->m_lock_count=0;
if(pbuffer->m_access == G_MA_WRITE_ONLY||pbuffer->m_access == G_MA_READ_WRITE)
{
gim_copy_buffers(&pbuffer->m_shadow_buffer,0,buffer_id,0,pbuffer->m_size);
}
}
else if(pbuffer->m_usage==G_MU_STATIC_WRITE_DYNAMIC_READ)
{
if(pbuffer->m_access == G_MA_READ_ONLY)
{
bm_data->m_prototype.unlock_buffer_fn(pbuffer->m_buffer_handle);
pbuffer->m_mapped_pointer = 0;
pbuffer->m_lock_count=0;
}
else
{
pbuffer->m_mapped_pointer = 0;
pbuffer->m_lock_count=0;
return G_BUFFER_OP_INVALID;
}
}
else if(pbuffer->m_usage==G_MU_DYNAMIC_READ_WRITE)
{
bm_data->m_prototype.unlock_buffer_fn(pbuffer->m_buffer_handle);
pbuffer->m_mapped_pointer = 0;
pbuffer->m_lock_count=0;
}
return G_BUFFER_OP_SUCCESS;
}
GINT gim_get_buffer_size(GBUFFER_ID * buffer_id,GUINT * buffer_size)
{
VALIDATE_BUFFER_ID_PT(buffer_id)
*buffer_size = pbuffer->m_size;
return G_BUFFER_OP_SUCCESS;
}
GINT gim_get_buffer_is_locked(GBUFFER_ID * buffer_id,GUINT * lock_count)
{
VALIDATE_BUFFER_ID_PT(buffer_id)
*lock_count = pbuffer->m_lock_count;
return G_BUFFER_OP_SUCCESS;
}
GINT gim_download_from_buffer(
GBUFFER_ID * buffer_id,
GUINT source_pos,
void * destdata,
GUINT copysize)
{
VALIDATE_BUFFER_ID_PT(buffer_id)
bm_data->m_prototype.download_from_buffer_fn(
pbuffer->m_buffer_handle,source_pos,destdata,copysize);
return G_BUFFER_OP_SUCCESS;
}
GINT gim_upload_to_buffer(
GBUFFER_ID * buffer_id,
GUINT dest_pos,
void * sourcedata,
GUINT copysize)
{
VALIDATE_BUFFER_ID_PT(buffer_id)
bm_data->m_prototype.upload_to_buffer_fn(
pbuffer->m_buffer_handle,dest_pos,sourcedata,copysize);
return G_BUFFER_OP_SUCCESS;
}
GINT gim_copy_buffers(
GBUFFER_ID * source_buffer_id,
GUINT source_pos,
GBUFFER_ID * dest_buffer_id,
GUINT dest_pos,
GUINT copysize)
{
GBUFFER_MANAGER_DATA * bm_data1,* bm_data2;
GBUFFER_DATA * pbuffer1, * pbuffer2;
void * tempdata;
if(_validate_buffer_id(source_buffer_id,&pbuffer1,&bm_data1)!= G_BUFFER_OP_SUCCESS) return G_BUFFER_OP_INVALID;
if(_validate_buffer_id(dest_buffer_id,&pbuffer2,&bm_data2)!= G_BUFFER_OP_SUCCESS) return G_BUFFER_OP_INVALID;
if((source_buffer_id->m_buffer_manager_id == dest_buffer_id->m_buffer_manager_id)||
(source_buffer_id->m_buffer_manager_id == G_BUFFER_MANAGER_SYSTEM && dest_buffer_id->m_buffer_manager_id == G_BUFFER_MANAGER_SHARED)||
(source_buffer_id->m_buffer_manager_id == G_BUFFER_MANAGER_SHARED && dest_buffer_id->m_buffer_manager_id == G_BUFFER_MANAGER_SYSTEM)
)
{//smooth copy
bm_data1->m_prototype.copy_buffers_fn(pbuffer1->m_buffer_handle,source_pos,pbuffer2->m_buffer_handle,dest_pos,copysize);
}
else if(source_buffer_id->m_buffer_manager_id == G_BUFFER_MANAGER_SYSTEM || source_buffer_id->m_buffer_manager_id == G_BUFFER_MANAGER_SHARED)
{
//hard copy
tempdata = (void *)pbuffer1->m_buffer_handle;
//upload data
bm_data2->m_prototype.upload_to_buffer_fn(pbuffer2->m_buffer_handle,dest_pos,
tempdata,
copysize);
}
else
{
//very hard copy
void * tempdata = gim_alloc(copysize);
//download data
bm_data1->m_prototype.download_from_buffer_fn(pbuffer1->m_buffer_handle,source_pos,
tempdata,
copysize);
//upload data
bm_data2->m_prototype.upload_to_buffer_fn(pbuffer2->m_buffer_handle,dest_pos,
tempdata,
copysize);
//delete temp buffer
gim_free(tempdata,copysize);
}
return G_BUFFER_OP_SUCCESS;
}
GINT gim_buffer_array_lock(GBUFFER_ARRAY * array_data, int access)
{
if(array_data->m_buffer_data != 0) return G_BUFFER_OP_SUCCESS;
GINT result = gim_lock_buffer(&array_data->m_buffer_id,access,&array_data->m_buffer_data);
if(result!= G_BUFFER_OP_SUCCESS) return result;
array_data->m_buffer_data += array_data->m_byte_offset;
return result;
}
GINT gim_buffer_array_unlock(GBUFFER_ARRAY * array_data)
{
if(array_data->m_buffer_data == 0) return G_BUFFER_OP_SUCCESS;
GINT result = gim_unlock_buffer(&array_data->m_buffer_id);
if(result!= G_BUFFER_OP_SUCCESS) return result;
array_data->m_buffer_data = 0;
return result;
}
void gim_buffer_array_copy_ref(GBUFFER_ARRAY * source_data,GBUFFER_ARRAY * dest_data)
{
dest_data->m_buffer_id.m_buffer_id = source_data->m_buffer_id.m_buffer_id;
dest_data->m_buffer_id.m_buffer_manager_id = source_data->m_buffer_id.m_buffer_manager_id;
dest_data->m_buffer_data = 0;
dest_data->m_byte_stride = source_data->m_byte_stride;
dest_data->m_byte_offset = source_data->m_byte_offset;
dest_data->m_element_count = source_data->m_element_count;
gim_buffer_add_ref(&dest_data->m_buffer_id);
}
void gim_buffer_array_copy_value(GBUFFER_ARRAY * source_data,GBUFFER_ARRAY * dest_data, GUINT buffer_manager_id,int usage)
{
//Create new buffer
GUINT buffsize = source_data->m_element_count*source_data->m_byte_stride;
gim_create_buffer(buffer_manager_id,buffsize,usage,&dest_data->m_buffer_id);
//copy ref data
dest_data->m_buffer_data = 0;
dest_data->m_byte_stride = source_data->m_byte_stride;
dest_data->m_byte_offset = 0;
dest_data->m_element_count = source_data->m_element_count;
gim_buffer_add_ref(&dest_data->m_buffer_id);
//copy buffers
gim_copy_buffers(&source_data->m_buffer_id,source_data->m_byte_offset,&dest_data->m_buffer_id,0,buffsize);
}

View File

@ -0,0 +1,251 @@
/*
-----------------------------------------------------------------------------
This source file is part of GIMPACT Library.
For the latest info, see http://gimpact.sourceforge.net/
Copyright (c) 2006 Francisco Leon. 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.
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 and GIMPACT-LICENSE-BSD.TXT for more details.
-----------------------------------------------------------------------------
*/
#include "GIMPACT/gim_trimesh.h"
#define FABS(x) (float(fabs(x))) /* implement as is fastest on your machine */
/* some macros */
#define CLASSIFY_TRIPOINTS_BY_FACE(v1,v2,v3,faceplane,out_of_face)\
{ \
_distances[0] = DISTANCE_PLANE_POINT(faceplane,v1);\
_distances[1] = _distances[0] * DISTANCE_PLANE_POINT(faceplane,v2);\
_distances[2] = _distances[0] * DISTANCE_PLANE_POINT(faceplane,v3); \
if(_distances[1]>0.0f && _distances[2]>0.0f)\
{\
out_of_face = 1;\
}\
else\
{\
out_of_face = 0;\
}\
}\
/* sort so that a<=b */
#define SORT(a,b) \
if(a>b) \
{ \
float c; \
c=a; \
a=b; \
b=c; \
}
/* this edge to edge test is 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 && d>=0 && d<=f) || (f<0 && d<=0 && d>=f)) \
{ \
e=Ax*Cy-Ay*Cx; \
if(f>0) \
{ \
if(e>=0 && e<=f) return 1; \
} \
else \
{ \
if(e<=0 && e>=f) return 1; \
} \
}
#define EDGE_AGAINST_TRI_EDGES(V0,V1,U0,U1,U2) \
{ \
float Ax,Ay,Bx,By,Cx,Cy,e,d,f; \
Ax=V1[i0]-V0[i0]; \
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); \
}
#define POINT_IN_TRI(V0,U0,U1,U2) \
{ \
float a,b,c,d0,d1,d2; \
/* is T1 completly inside T2? */ \
/* check if V0 is inside tri(U0,U1,U2) */ \
a=U1[i1]-U0[i1]; \
b=-(U1[i0]-U0[i0]); \
c=-a*U0[i0]-b*U0[i1]; \
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]; \
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]; \
d2=a*V0[i0]+b*V0[i1]+c; \
if(d0*d1>0.0) \
{ \
if(d0*d2>0.0) return 1; \
} \
}
int coplanar_tri_tri(GIM_TRIANGLE_DATA *tri1,
GIM_TRIANGLE_DATA *tri2)
{
short i0,i1;
/* first project onto an axis-aligned plane, that maximizes the area */
/* of the triangles, compute indices: i0,i1. */
PLANE_MINOR_AXES(tri1->m_planes.m_planes[0], i0, i1);
/* test all edges of triangle 1 against the edges of triangle 2 */
EDGE_AGAINST_TRI_EDGES(tri1->m_vertices[0],tri1->m_vertices[1],tri2->m_vertices[0],tri2->m_vertices[1],tri2->m_vertices[2]);
EDGE_AGAINST_TRI_EDGES(tri1->m_vertices[1],tri1->m_vertices[2],tri2->m_vertices[0],tri2->m_vertices[1],tri2->m_vertices[2]);
EDGE_AGAINST_TRI_EDGES(tri1->m_vertices[2],tri1->m_vertices[0],tri2->m_vertices[0],tri2->m_vertices[1],tri2->m_vertices[2]);
/* finally, test if tri1 is totally contained in tri2 or vice versa */
POINT_IN_HULL(tri1->m_vertices[0],(&tri2->m_planes.m_planes[1]),3,i0);
if(i0==0) return 1;
POINT_IN_HULL(tri2->m_vertices[0],(&tri1->m_planes.m_planes[1]),3,i0);
if(i0==0) return 1;
return 0;
}
#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 coplanar_tri_tri(tri1,tri2); \
} \
}\
int gim_triangle_triangle_overlap(
GIM_TRIANGLE_DATA *tri1,
GIM_TRIANGLE_DATA *tri2)
{
vec3f _distances;
char out_of_face;
CLASSIFY_TRIPOINTS_BY_FACE(tri1->m_vertices[0],tri1->m_vertices[1],tri1->m_vertices[2],tri2->m_planes.m_planes[0],out_of_face);
if(out_of_face==1) return 0;
CLASSIFY_TRIPOINTS_BY_FACE(tri2->m_vertices[0],tri2->m_vertices[1],tri2->m_vertices[2],tri1->m_planes.m_planes[0],out_of_face);
if(out_of_face==1) return 0;
float du0=0,du1=0,du2=0,dv0=0,dv1=0,dv2=0;
float D[3];
float isect1[2], isect2[2];
float du0du1=0,du0du2=0,dv0dv1=0,dv0dv2=0;
short index;
float vp0,vp1,vp2;
float up0,up1,up2;
float bb,cc,max;
/* compute direction of intersection line */
VEC_CROSS(D,tri1->m_planes.m_planes[0],tri2->m_planes.m_planes[0]);
/* compute and index to the largest component of D */
max=(float)FABS(D[0]);
index=0;
bb=(float)FABS(D[1]);
cc=(float)FABS(D[2]);
if(bb>max) max=bb,index=1;
if(cc>max) max=cc,index=2;
/* this is the simplified projection onto L*/
vp0= tri1->m_vertices[0][index];
vp1= tri1->m_vertices[1][index];
vp2= tri1->m_vertices[2][index];
up0= tri2->m_vertices[0][index];
up1= tri2->m_vertices[1][index];
up2= tri2->m_vertices[2][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);
float xx,yy,xxyy,tmp;
xx=x0*x1;
yy=y0*y1;
xxyy=xx*yy;
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]<isect2[0] || isect2[1]<isect1[0]) return 0;
return 1;
}

View File

@ -0,0 +1,364 @@
/*
-----------------------------------------------------------------------------
This source file is part of GIMPACT Library.
For the latest info, see http://gimpact.sourceforge.net/
Copyright (c) 2006 Francisco Leon. 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.
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 and GIMPACT-LICENSE-BSD.TXT for more details.
-----------------------------------------------------------------------------
*/
#include <assert.h>
#include "GIMPACT/gim_trimesh.h"
GUINT gim_trimesh_get_triangle_count(GIM_TRIMESH * trimesh)
{
return trimesh->m_tri_index_buffer.m_element_count/3;
}
//! Creates the aabb set and the triangles cache
/*!
\param trimesh
\param vertex_array
\param triindex_array
\param transformed_reply If 1, then the m_transformed_vertices is a reply of the source vertices. Else it just be a reference to the original array.
\post it copies the arrays by reference, and creates the auxiliary data (m_aabbset,m_planes_cache_buffer)
*/
void gim_trimesh_create_from_arrays(GIM_TRIMESH * trimesh, GBUFFER_ARRAY * vertex_array, GBUFFER_ARRAY * triindex_array,char transformed_reply)
{
assert(trimesh);
assert(vertex_array);
assert(triindex_array);
gim_buffer_array_copy_ref(vertex_array,&trimesh->m_source_vertex_buffer);
gim_buffer_array_copy_ref(triindex_array,&trimesh->m_tri_index_buffer);
trimesh->m_mask = GIM_TRIMESH_NEED_UPDATE;//needs update
//Create the transformed vertices
if(transformed_reply==1)
{
trimesh->m_mask |= GIM_TRIMESH_TRANSFORMED_REPLY;
gim_buffer_array_copy_value(vertex_array,&trimesh->m_transformed_vertex_buffer,G_BUFFER_MANAGER_SYSTEM,G_MU_DYNAMIC_READ_WRITE);
}
else
{
gim_buffer_array_copy_ref(vertex_array,&trimesh->m_transformed_vertex_buffer);
}
//create the box set
GUINT facecount = gim_trimesh_get_triangle_count(trimesh);
gim_aabbset_alloc(&trimesh->m_aabbset,facecount);
//create the planes cache
GIM_DYNARRAY_CREATE_SIZED(GIM_TRIPLANES_CACHE,trimesh->m_planes_cache_buffer,facecount);
//Create the bitset
GIM_BITSET_CREATE_SIZED(trimesh->m_planes_cache_bitset,facecount);
//Callback is 0
trimesh->m_update_callback = 0;
//set to identity
IDENTIFY_MATRIX_4X4(trimesh->m_transform);
}
//! Create a trimesh from vertex array and an index array
/*!
\param trimesh An uninitialized GIM_TRIMESH structure
\param vertex_array A buffer to a vec3f array
\param vertex_count
\param triindex_array
\param index_count
\param copy_vertices If 1, it copies the source vertices in another buffer. Else (0) it constructs a reference to the data.
\param copy_indices If 1, it copies the source vertices in another buffer. Else (0) it constructs a reference to the data.
\param transformed_reply If , then the m_transformed_vertices is a reply of the source vertices. Else it just be a reference to the original array.
*/
void gim_trimesh_create_from_data(GIM_TRIMESH * trimesh, vec3f * vertex_array, GUINT vertex_count,char copy_vertices, GUINT * triindex_array, GUINT index_count,char copy_indices,char transformed_reply)
{
GBUFFER_ARRAY buffer_vertex_array;
GBUFFER_ARRAY buffer_triindex_array;
//Create vertices
if(copy_vertices == 1)
{
gim_create_common_buffer_from_data(vertex_array, vertex_count*sizeof(vec3f), &buffer_vertex_array.m_buffer_id);
}
else//Create a shared buffer
{
gim_create_shared_buffer_from_data(vertex_array, vertex_count*sizeof(vec3f), &buffer_vertex_array.m_buffer_id);
}
GIM_BUFFER_ARRAY_INIT_TYPE(vec3f,buffer_vertex_array,buffer_vertex_array.m_buffer_id,vertex_count);
//Create vertices
if(copy_indices == 1)
{
gim_create_common_buffer_from_data(triindex_array, index_count*sizeof(GUINT), &buffer_triindex_array.m_buffer_id);
}
else//Create a shared buffer
{
gim_create_shared_buffer_from_data(triindex_array, index_count*sizeof(GUINT), &buffer_triindex_array.m_buffer_id);
}
GIM_BUFFER_ARRAY_INIT_TYPE(GUINT,buffer_triindex_array,buffer_triindex_array.m_buffer_id,index_count);
gim_trimesh_create_from_arrays(trimesh, &buffer_vertex_array, &buffer_triindex_array,transformed_reply);
///always call this after create a buffer_array
GIM_BUFFER_ARRAY_DESTROY(buffer_vertex_array);
GIM_BUFFER_ARRAY_DESTROY(buffer_triindex_array);
}
//! Clears auxiliary data and releases buffer arrays
void gim_trimesh_destroy(GIM_TRIMESH * trimesh)
{
gim_aabbset_destroy(&trimesh->m_aabbset);
GIM_DYNARRAY_DESTROY(trimesh->m_planes_cache_buffer);
GIM_DYNARRAY_DESTROY(trimesh->m_planes_cache_bitset);
GIM_BUFFER_ARRAY_DESTROY(trimesh->m_transformed_vertex_buffer);
GIM_BUFFER_ARRAY_DESTROY(trimesh->m_source_vertex_buffer);
GIM_BUFFER_ARRAY_DESTROY(trimesh->m_tri_index_buffer);
}
//! Copies two meshes
/*!
\pre dest_trimesh shouldn't be created
\post dest_trimesh will be created
\param source_trimesh
\param dest_trimesh
\param copy_by_reference If 1, it attach a reference to the source vertices, else it copies the vertices
\param transformed_reply IF 1, then it forces the m_trasnformed_vertices to be a reply of the source vertices
*/
void gim_trimesh_copy(GIM_TRIMESH * source_trimesh,GIM_TRIMESH * dest_trimesh, char copy_by_reference, char transformed_reply)
{
if(copy_by_reference==1)
{
gim_trimesh_create_from_arrays(dest_trimesh, &source_trimesh->m_source_vertex_buffer, &source_trimesh->m_tri_index_buffer,transformed_reply);
}
else
{
GBUFFER_ARRAY buffer_vertex_array;
GBUFFER_ARRAY buffer_triindex_array;
gim_buffer_array_copy_value(&source_trimesh->m_source_vertex_buffer,&buffer_vertex_array,G_BUFFER_MANAGER_SYSTEM,G_MU_DYNAMIC_READ_WRITE);
gim_buffer_array_copy_value(&source_trimesh->m_tri_index_buffer,&buffer_triindex_array,G_BUFFER_MANAGER_SYSTEM,G_MU_DYNAMIC_READ_WRITE);
gim_trimesh_create_from_arrays(dest_trimesh, &buffer_vertex_array, &buffer_triindex_array,transformed_reply);
///always call this after create a buffer_array
GIM_BUFFER_ARRAY_DESTROY(buffer_vertex_array);
GIM_BUFFER_ARRAY_DESTROY(buffer_triindex_array);
}
}
//! Locks the trimesh for working with it
/*!
\post locks m_tri_index_buffer and m_transformed_vertex_buffer.
\param trimesh
*/
void gim_trimesh_locks_work_data(GIM_TRIMESH * trimesh)
{
GINT res;
res=gim_buffer_array_lock(&trimesh->m_tri_index_buffer,G_MA_READ_ONLY);
assert(res==G_BUFFER_OP_SUCCESS);
res=gim_buffer_array_lock(&trimesh->m_transformed_vertex_buffer,G_MA_READ_ONLY);
assert(res==G_BUFFER_OP_SUCCESS);
}
//! unlocks the trimesh
/*!
\post unlocks m_tri_index_buffer and m_transformed_vertex_buffer.
\param trimesh
*/
void gim_trimesh_unlocks_work_data(GIM_TRIMESH * trimesh)
{
gim_buffer_array_unlock(&trimesh->m_tri_index_buffer);
gim_buffer_array_unlock(&trimesh->m_transformed_vertex_buffer);
}
//! Returns 1 if the m_transformed_vertex_buffer is a reply of m_source_vertex_buffer
char gim_trimesh_has_tranformed_reply(GIM_TRIMESH * trimesh)
{
if(trimesh->m_mask&GIM_TRIMESH_TRANSFORMED_REPLY) return 1;
return 0;
}
//! Returns 1 if the trimesh needs to update their aabbset and the planes cache.
char gim_trimesh_needs_update(GIM_TRIMESH * trimesh)
{
if(trimesh->m_mask&GIM_TRIMESH_NEED_UPDATE) return 1;
return 0;
}
//! Change the state of the trimesh for force it to update
/*!
Call it after made changes to the trimesh.
\post gim_trimesh_need_update(trimesh) will return 1
*/
void gim_trimesh_post_update(GIM_TRIMESH * trimesh)
{
trimesh->m_mask |= GIM_TRIMESH_NEED_UPDATE;
}
//kernel
#define MULT_MAT_VEC4_KERNEL(_mat,_src,_dst) MAT_DOT_VEC_3X4((_dst),(_mat),(_src))
//! Updates m_transformed_vertex_buffer
/*!
\pre m_transformed_vertex_buffer must be unlocked
*/
void gim_trimesh_update_vertices(GIM_TRIMESH * trimesh)
{
if(gim_trimesh_has_tranformed_reply(trimesh) == 0) return; //Don't perform transformation
//Vertices
GBUFFER_ARRAY * psource_vertex_buffer = &trimesh->m_source_vertex_buffer;
GBUFFER_ARRAY * ptransformed_vertex_buffer = &trimesh->m_transformed_vertex_buffer;
//Temp transform
mat4f transform;
COPY_MATRIX_4X4(transform,trimesh->m_transform);
GIM_PROCESS_BUFFER_ARRAY(transform,(*psource_vertex_buffer),(*ptransformed_vertex_buffer),MULT_MAT_VEC4_KERNEL,vec3f,vec3f);
}
//! Updates m_aabbset and m_planes_cache_bitset
/*!
\pre gim_trimesh_locks_work_data must be called before
*/
void gim_trimesh_update_aabbset(GIM_TRIMESH * trimesh)
{
vec3f * transformed_vertices = GIM_BUFFER_ARRAY_POINTER(vec3f,trimesh->m_transformed_vertex_buffer,0);
assert(transformed_vertices);
GUINT * triangle_indices = GIM_BUFFER_ARRAY_POINTER(GUINT,trimesh->m_tri_index_buffer,0);
assert(triangle_indices);
// box set
aabb3f * paabb = trimesh->m_aabbset.m_boxes;
GUINT triangle_count = gim_trimesh_get_triangle_count(trimesh);
float * v1,*v2,*v3;
GUINT i;
for (i=0; i<triangle_count;i++)
{
v1 = &transformed_vertices[triangle_indices[0]][0];
v2 = &transformed_vertices[triangle_indices[1]][0];
v3 = &transformed_vertices[triangle_indices[2]][0];
COMPUTEAABB_FOR_TRIANGLE((*paabb),v1,v2,v3);
triangle_indices+=3;
paabb++;
}
//Clear planes cache
GIM_BITSET_CLEAR_ALL(trimesh->m_planes_cache_bitset);
//Sorts set
gim_aabbset_update(&trimesh->m_aabbset);
}
//! Updates the trimesh if needed
/*!
\post If gim_trimesh_needs_update returns 1, then it calls gim_trimesh_update_vertices and gim_trimesh_update_aabbset
*/
void gim_trimesh_update(GIM_TRIMESH * trimesh)
{
if(gim_trimesh_needs_update(trimesh)==0) return;
gim_trimesh_update_vertices(trimesh);
gim_trimesh_locks_work_data(trimesh);
gim_trimesh_update_aabbset(trimesh);
gim_trimesh_unlocks_work_data(trimesh);
//Clear update flag
trimesh->m_mask &= ~GIM_TRIMESH_NEED_UPDATE;
}
void gim_trimesh_set_tranform(GIM_TRIMESH * trimesh, mat4f transform)
{
GREAL diff = 0.0f;
float * originaltrans = &trimesh->m_transform[0][0];
float * newtrans = &transform[0][0];
GUINT i;
for (i=0;i<16;i++)
{
diff += fabs(originaltrans[i]-newtrans[i]);
}
// if(IS_ZERO(diff)) return ;///don't need to update
if(diff< 0.00001f) return ;///don't need to update
COPY_MATRIX_4X4(trimesh->m_transform,transform);
gim_trimesh_post_update(trimesh);
}
void gim_trimesh_get_triangle_data(GIM_TRIMESH * trimesh, GUINT triangle_index, GIM_TRIANGLE_DATA * tri_data)
{
vec3f * transformed_vertices = GIM_BUFFER_ARRAY_POINTER(vec3f,trimesh->m_transformed_vertex_buffer,0);
GUINT * triangle_indices = GIM_BUFFER_ARRAY_POINTER(GUINT,trimesh->m_tri_index_buffer,triangle_index*3);
//Copy the vertices
VEC_COPY(tri_data->m_vertices[0],transformed_vertices[triangle_indices[0]]);
VEC_COPY(tri_data->m_vertices[1],transformed_vertices[triangle_indices[1]]);
VEC_COPY(tri_data->m_vertices[2],transformed_vertices[triangle_indices[2]]);
//Get the planes
GIM_TRIPLANES_CACHE * planes = GIM_DYNARRAY_POINTER(GIM_TRIPLANES_CACHE,trimesh->m_planes_cache_buffer);
planes += triangle_index;
//verify planes cache
GUINT bit_eval;
GIM_BITSET_GET(trimesh->m_planes_cache_bitset,triangle_index,bit_eval);
if(bit_eval == 0)// Needs to calc the planes
{
//Calc the face plane
TRIANGLE_PLANE(tri_data->m_vertices[0],tri_data->m_vertices[1],tri_data->m_vertices[2],planes->m_planes[0]);
//Calc the edge 1
EDGE_PLANE(tri_data->m_vertices[0],tri_data->m_vertices[1],(planes->m_planes[0]),(planes->m_planes[1]));
//Calc the edge 2
EDGE_PLANE(tri_data->m_vertices[1],tri_data->m_vertices[2],(planes->m_planes[0]),(planes->m_planes[2]));
//Calc the edge 3
EDGE_PLANE(tri_data->m_vertices[2],tri_data->m_vertices[0],(planes->m_planes[0]), (planes->m_planes[3]));
//mark
GIM_BITSET_SET(trimesh->m_planes_cache_bitset,triangle_index);
}
VEC_COPY_4((tri_data->m_planes.m_planes[0]),(planes->m_planes[0]));//face plane
VEC_COPY_4((tri_data->m_planes.m_planes[1]),(planes->m_planes[1]));//edge1
VEC_COPY_4((tri_data->m_planes.m_planes[2]),(planes->m_planes[2]));//edge2
VEC_COPY_4((tri_data->m_planes.m_planes[3]),(planes->m_planes[3]));//edge3
}
void gim_trimesh_get_triangle_vertices(GIM_TRIMESH * trimesh, GUINT triangle_index, vec3f v1,vec3f v2,vec3f v3)
{
vec3f * transformed_vertices = GIM_BUFFER_ARRAY_POINTER(vec3f,trimesh->m_transformed_vertex_buffer,0);
GUINT * triangle_indices = GIM_BUFFER_ARRAY_POINTER(GUINT,trimesh->m_tri_index_buffer,triangle_index*3);
//Copy the vertices
VEC_COPY(v1,transformed_vertices[triangle_indices[0]]);
VEC_COPY(v2,transformed_vertices[triangle_indices[1]]);
VEC_COPY(v3,transformed_vertices[triangle_indices[2]]);
}

View File

@ -0,0 +1,279 @@
/*
-----------------------------------------------------------------------------
This source file is part of GIMPACT Library.
For the latest info, see http://gimpact.sourceforge.net/
Copyright (c) 2006 Francisco Leon. 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.
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 and GIMPACT-LICENSE-BSD.TXT for more details.
-----------------------------------------------------------------------------
*/
#include "GIMPACT/gim_trimesh.h"
//! Utility function for find the closest point between a segment and a triangle
/*!
\param triangle
\param s1
\param s2
\param contacts Contains the closest points on the segment (1,2), and the normal points to segment, and m_depth contains the distance
\post The contacts array is not set to 0. It adds aditional contacts
*/
void gim_closest_point_triangle_segment(GIM_TRIANGLE_DATA * triangle, vec3f s1,vec3f s2, GDYNAMIC_ARRAY * contacts)
{
vec3f segment_points[4];
vec3f closest_points[2];
GUINT intersection_type, out_edge= 10;
GREAL dis, dis_temp,perpend;
vec4f sdiff;
dis = DISTANCE_PLANE_POINT(triangle->m_planes.m_planes[0],s1);
dis_temp = DISTANCE_PLANE_POINT(triangle->m_planes.m_planes[0],s2);
if(dis<=0.0f && dis_temp<=0.0f) return;
VEC_DIFF(sdiff,s2,s1);
perpend = VEC_DOT(sdiff,triangle->m_planes.m_planes[0]);
if(!IS_ZERO(perpend)) // Not perpendicular
{
if(dis<dis_temp)
{
VEC_COPY(closest_points[0],s1);
}
else
{
dis = dis_temp;
VEC_COPY(closest_points[0],s2);
}
//Testing segment vertices over triangle
if(dis>=0.0f && dis_temp>=0.0f)
{
POINT_IN_HULL(closest_points[0],(&triangle->m_planes.m_planes[1]),3,out_edge);
if(out_edge==0)//Point over face
{
GIM_PUSH_CONTACT((*contacts),closest_points[0] ,triangle->m_planes.m_planes[0] ,dis,0, 0, 0,0);
return;
}
}
else
{
PLANE_CLIP_SEGMENT(s1,s2,triangle->m_planes.m_planes[0],closest_points[1]);
POINT_IN_HULL(closest_points[1],(&triangle->m_planes.m_planes[1]),3,out_edge);
if(out_edge==0)//Point over face
{
GIM_PUSH_CONTACT((*contacts),closest_points[0] ,triangle->m_planes.m_planes[0] ,dis,0, 0, 0,0);
return;
}
}
}
else // Perpendicular Face
{
//out_edge=10
//Clip segment by triangle
// Edge1
PLANE_CLIP_SEGMENT_CLOSEST(s1,s2,triangle->m_planes.m_planes[1],segment_points[0],segment_points[1],intersection_type);
if(intersection_type==0||intersection_type==1)
{
out_edge = 0;
VEC_COPY(closest_points[0],segment_points[0]);
}
else
{
//Edge2
PLANE_CLIP_SEGMENT_CLOSEST(segment_points[0],segment_points[1],triangle->m_planes.m_planes[2],segment_points[2],segment_points[3],intersection_type);
if(intersection_type==0||intersection_type==1)
{
out_edge = 1;
VEC_COPY(closest_points[0],segment_points[3]);
}
else
{
//Edge3
PLANE_CLIP_SEGMENT_CLOSEST(segment_points[2],segment_points[3],triangle->m_planes.m_planes[3],closest_points[0],closest_points[1],intersection_type);
if(intersection_type==0||intersection_type==1)
{
out_edge = 2;
}
}
}
//POST closest_points[0] and closest_points[1] are inside the triangle, if out_edge>2
if(out_edge>2) // Over triangle
{
dis = VEC_DOT(closest_points[0],triangle->m_planes.m_planes[0]);
GIM_PUSH_CONTACT((*contacts),closest_points[0] ,triangle->m_planes.m_planes[0] ,dis,0, 0, 0,0);
GIM_PUSH_CONTACT((*contacts),closest_points[1] ,triangle->m_planes.m_planes[0] ,dis,0, 0, 0,0);
return;
}
}
//Find closest edges
out_edge = 10;
dis = G_REAL_INFINITY;
GUINT i;
for(i=0;i<3;i++)
{
SEGMENT_COLLISION(s1,s2,triangle->m_vertices[i],triangle->m_vertices[(i+1)%3],segment_points[0],segment_points[1]);
VEC_DIFF(sdiff,segment_points[0],segment_points[1]);
dis_temp = VEC_DOT(sdiff,sdiff);
if(dis_temp< dis)
{
dis = dis_temp;
out_edge = i;
VEC_COPY(closest_points[0],segment_points[0]);
VEC_COPY(closest_points[1],sdiff);//normal
}
}
if(out_edge>2) return ;// ???? ASSERT this please
if(IS_ZERO(dis))
{
//Set face plane
GIM_PUSH_CONTACT((*contacts),closest_points[0] ,triangle->m_planes.m_planes[0] ,0.0f,0, 0, 0,0);
}
else
{
GIM_SQRT(dis,dis);
VEC_SCALE(closest_points[1],(1.0f/dis),closest_points[1]);//normal
GIM_PUSH_CONTACT((*contacts),closest_points[0] ,closest_points[1],dis,0, 0, 0,0);
}
}
//! Utility function for find the closest point between a capsule and a triangle
/*!
\param triangle
\param capsule
\param contacts Contains the closest points on the capsule, and the normal points to triangle
\post The contacts array is not set to 0. It adds aditional contacts
*/
int gim_triangle_capsule_collision(GIM_TRIANGLE_DATA * triangle, GIM_CAPSULE_DATA * capsule, GDYNAMIC_ARRAY * contacts)
{
GUINT old_contact_size = contacts->m_size;
gim_closest_point_triangle_segment(triangle,capsule->m_point1,capsule->m_point2,contacts);
GIM_CONTACT * pcontact = GIM_DYNARRAY_POINTER(GIM_CONTACT ,(*contacts));
pcontact+= old_contact_size;
if(pcontact->m_depth > capsule->m_radius)
{
contacts->m_size = old_contact_size;
return 0;
}
vec3f vec;
while(old_contact_size<contacts->m_size)
{
//Scale the normal for pointing to triangle
VEC_SCALE(pcontact->m_normal,-1.0f,pcontact->m_normal);
//Fix the contact point
VEC_SCALE(vec,capsule->m_radius,pcontact->m_normal);
VEC_SUM(pcontact->m_point,vec,pcontact->m_point);
//Fix the depth
pcontact->m_depth = capsule->m_radius - pcontact->m_depth;
pcontact++;
old_contact_size++;
}
return 1;
}
//! Trimesh Capsule collision
/*!
Find the closest primitive collided by the ray
\param trimesh
\param capsule
\param contact
\param contacts A GIM_CONTACT array. Must be initialized
*/
void gim_trimesh_capsule_collision(GIM_TRIMESH * trimesh, GIM_CAPSULE_DATA * capsule, GDYNAMIC_ARRAY * contacts)
{
contacts->m_size = 0;
aabb3f test_aabb;
CALC_CAPSULE_AABB((*capsule),test_aabb);
GDYNAMIC_ARRAY collision_result;
GIM_CREATE_BOXQUERY_LIST(collision_result);
gim_aabbset_box_collision(&test_aabb, &trimesh->m_aabbset , &collision_result);
if(collision_result.m_size==0)
{
GIM_DYNARRAY_DESTROY(collision_result);
}
//collide triangles
//Locks trimesh
gim_trimesh_locks_work_data(trimesh);
//dummy contacts
GDYNAMIC_ARRAY dummycontacts;
GIM_CREATE_CONTACT_LIST(dummycontacts);
int cresult;
unsigned int i;
GUINT * boxesresult = GIM_DYNARRAY_POINTER(GUINT,collision_result);
GIM_TRIANGLE_DATA tri_data;
GUINT old_contact_size;
GIM_CONTACT * pcontact;
for(i=0;i<collision_result.m_size;i++)
{
old_contact_size = dummycontacts.m_size;
gim_trimesh_get_triangle_data(trimesh,boxesresult[i],&tri_data);
cresult = gim_triangle_capsule_collision(&tri_data, capsule, &dummycontacts);
if(cresult!=0)
{
pcontact = GIM_DYNARRAY_POINTER(GIM_CONTACT ,dummycontacts);
pcontact+= old_contact_size;
while(old_contact_size<dummycontacts.m_size)
{
pcontact->m_handle1 = trimesh;
pcontact->m_handle2 = capsule;
pcontact->m_feature1 = boxesresult[i];
pcontact->m_feature2 = 0;
pcontact++;
old_contact_size++;
}
}
}
///unlocks
gim_trimesh_unlocks_work_data(trimesh);
///Destroy box result
GIM_DYNARRAY_DESTROY(collision_result);
//merge contacts
gim_merge_contacts(&dummycontacts,contacts);
//Destroy dummy
GIM_DYNARRAY_DESTROY(dummycontacts);
}

View File

@ -0,0 +1,140 @@
/*
-----------------------------------------------------------------------------
This source file is part of GIMPACT Library.
For the latest info, see http://gimpact.sourceforge.net/
Copyright (c) 2006 Francisco Leon. 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.
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 and GIMPACT-LICENSE-BSD.TXT for more details.
-----------------------------------------------------------------------------
*/
#include "GIMPACT/gim_trimesh.h"
//! Trimesh Ray Collisions
/*!
\param trimesh
\param contact
\return 1 if the ray collides, else 0
*/
int gim_trimesh_ray_collision(GIM_TRIMESH * trimesh,vec3f origin,vec3f dir, GREAL tmax, GIM_TRIANGLE_RAY_CONTACT_DATA * contact)
{
GDYNAMIC_ARRAY collision_result;
GIM_CREATE_BOXQUERY_LIST(collision_result);
gim_aabbset_ray_collision(origin,dir,tmax,&trimesh->m_aabbset,&collision_result);
if(collision_result.m_size==0)
{
GIM_DYNARRAY_DESTROY(collision_result);
return 0;
}
//collide triangles
GUINT * boxesresult = GIM_DYNARRAY_POINTER(GUINT,collision_result);
GIM_TRIANGLE_DATA tridata;
vec3f pout;
GREAL tparam,u,v;
char does_intersect;
gim_trimesh_locks_work_data(trimesh);
for(unsigned int i=0;i<collision_result.m_size;i++)
{
gim_trimesh_get_triangle_data(trimesh,boxesresult[i],&tridata);
RAY_TRIANGLE_INTERSECTION(origin,dir,tridata.m_vertices[0],tridata.m_vertices[1],tridata.m_vertices[2],tridata.m_planes.m_planes[0],pout,u,v,tparam,tmax,does_intersect);
if(does_intersect)
{
contact->tparam = tparam;
contact->u = u;
contact->v = v;
contact->m_face_id = boxesresult[i];
VEC_COPY(contact->m_point,pout);
VEC_COPY(contact->m_normal,tridata.m_planes.m_planes[0]);
gim_trimesh_unlocks_work_data(trimesh);
GIM_DYNARRAY_DESTROY(collision_result);
return 1;
}
}
gim_trimesh_unlocks_work_data(trimesh);
GIM_DYNARRAY_DESTROY(collision_result);
return 0;//no collisiion
}
//! Trimesh Ray Collisions closest
/*!
Find the closest primitive collided by the ray
\param trimesh
\param contact
\return 1 if the ray collides, else 0
*/
int gim_trimesh_ray_closest_collision(GIM_TRIMESH * trimesh,vec3f origin,vec3f dir, GREAL tmax, GIM_TRIANGLE_RAY_CONTACT_DATA * contact)
{
GDYNAMIC_ARRAY collision_result;
GIM_CREATE_BOXQUERY_LIST(collision_result);
gim_aabbset_ray_collision(origin,dir,tmax,&trimesh->m_aabbset,&collision_result);
if(collision_result.m_size==0)
{
GIM_DYNARRAY_DESTROY(collision_result);
return 0;
}
//collide triangles
GUINT * boxesresult = GIM_DYNARRAY_POINTER(GUINT,collision_result);
GIM_TRIANGLE_DATA tridata;
vec3f pout;
GREAL tparam,u,v;
char does_intersect;
contact->tparam = tmax + 0.1f;
gim_trimesh_locks_work_data(trimesh);
for(unsigned int i=0;i<collision_result.m_size;i++)
{
gim_trimesh_get_triangle_data(trimesh,boxesresult[i],&tridata);
RAY_TRIANGLE_INTERSECTION(origin,dir,tridata.m_vertices[0],tridata.m_vertices[1],tridata.m_vertices[2],tridata.m_planes.m_planes[0],pout,u,v,tparam,tmax,does_intersect);
if(does_intersect && (tparam < contact->tparam))
{
contact->tparam = tparam;
contact->u = u;
contact->v = v;
contact->m_face_id = boxesresult[i];
VEC_COPY(contact->m_point,pout);
VEC_COPY(contact->m_normal,tridata.m_planes.m_planes[0]);
}
}
gim_trimesh_unlocks_work_data(trimesh);
GIM_DYNARRAY_DESTROY(collision_result);
if(contact->tparam > tmax) return 0;
return 1;
}

View File

@ -0,0 +1,196 @@
/*
-----------------------------------------------------------------------------
This source file is part of GIMPACT Library.
For the latest info, see http://gimpact.sourceforge.net/
Copyright (c) 2006 Francisco Leon. 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.
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 and GIMPACT-LICENSE-BSD.TXT for more details.
-----------------------------------------------------------------------------
*/
#include "GIMPACT/gim_trimesh.h"
int gim_triangle_sphere_collision(
GIM_TRIANGLE_DATA *tri,
vec3f center, GREAL radius,
GIM_TRIANGLE_CONTACT_DATA * contact_data)
{
contact_data->m_point_count = 0;
//Find Face plane distance
GREAL dis = DISTANCE_PLANE_POINT(tri->m_planes.m_planes[0],center);
if(dis>radius) return 0; //out
if(dis<-radius) return 0;//Out of triangle
contact_data->m_penetration_depth = dis;
//Find the most edge
GUINT most_edge = 4;//no edge
GREAL max_dis = 0.0f;
dis = DISTANCE_PLANE_POINT(tri->m_planes.m_planes[1],center);
if(dis>radius) return 0;//Out of triangle
if(dis>0.0f)
{
max_dis = dis;
most_edge = 0;
}
dis = DISTANCE_PLANE_POINT(tri->m_planes.m_planes[2],center);
if(dis>radius) return 0;//Out of triangle
if(dis>max_dis)// && dis>0.0f)
{
max_dis = dis;
most_edge = 1;
}
dis = DISTANCE_PLANE_POINT(tri->m_planes.m_planes[3],center);
if(dis>radius) return 0;//Out of triangle
if(dis>max_dis)// && dis>0.0f)
{
max_dis = dis;
most_edge = 2;
}
if(most_edge == 4) //Box is into triangle
{
//contact_data->m_penetration_depth = dis is set above
//Find Face plane point
VEC_COPY(contact_data->m_separating_normal,tri->m_planes.m_planes[0]);
//Find point projection on plane
if(contact_data->m_penetration_depth>=0.0f)
{
VEC_SCALE(contact_data->m_points[0],-radius,contact_data->m_separating_normal);
}
else
{
VEC_SCALE(contact_data->m_points[0],radius,contact_data->m_separating_normal);
}
contact_data->m_penetration_depth = radius - contact_data->m_penetration_depth;
VEC_SUM(contact_data->m_points[0],contact_data->m_points[0],center);
//Scale normal for pointing to triangle
VEC_SCALE(contact_data->m_separating_normal,-1.0f,contact_data->m_separating_normal);
contact_data->m_point_count = 1;
return 1;
}
//find the edge
vec3f e1,e2;
VEC_COPY(e1,tri->m_vertices[most_edge]);
VEC_COPY(e2,tri->m_vertices[(most_edge+1)%3]);
CLOSEST_POINT_ON_SEGMENT(contact_data->m_points[0],center,e1,e2);
//find distance
VEC_DIFF(e1,center,contact_data->m_points[0]);
VEC_LENGTH(e1,dis);
if(dis>radius) return 0;
contact_data->m_penetration_depth = radius - dis;
if(IS_ZERO(dis))
{
VEC_COPY(contact_data->m_separating_normal,tri->m_planes.m_planes[most_edge+1]);
VEC_SCALE(contact_data->m_points[0],-radius,contact_data->m_separating_normal);
VEC_SUM(contact_data->m_points[0],contact_data->m_points[0],center);
}
else
{
VEC_SCALE(contact_data->m_separating_normal,1.0f/dis,e1);
VEC_SCALE(contact_data->m_points[0],-radius,contact_data->m_separating_normal);
VEC_SUM(contact_data->m_points[0],contact_data->m_points[0],center);
}
//Scale normal for pointing to triangle
VEC_SCALE(contact_data->m_separating_normal,-1.0f,contact_data->m_separating_normal);
contact_data->m_point_count = 1;
return 1;
}
//! Trimesh Sphere Collisions
/*!
In each contact
<ul>
<li> m_handle1 points to trimesh.
<li> m_handle2 points to NULL.
<li> m_feature1 Is a triangle index of trimesh.
</ul>
\param trimesh
\param center
\param radius
\param contacts A GIM_CONTACT array. Must be initialized
*/
void gim_trimesh_sphere_collision(GIM_TRIMESH * trimesh,vec3f center,GREAL radius, GDYNAMIC_ARRAY * contacts)
{
contacts->m_size = 0;
aabb3f test_aabb;
test_aabb.minX = center[0]-radius;
test_aabb.maxX = center[0]+radius;
test_aabb.minY = center[1]-radius;
test_aabb.maxY = center[1]+radius;
test_aabb.minZ = center[2]-radius;
test_aabb.maxZ = center[2]+radius;
GDYNAMIC_ARRAY collision_result;
GIM_CREATE_BOXQUERY_LIST(collision_result);
gim_aabbset_box_collision(&test_aabb, &trimesh->m_aabbset , &collision_result);
if(collision_result.m_size==0)
{
GIM_DYNARRAY_DESTROY(collision_result);
}
//collide triangles
//Locks trimesh
gim_trimesh_locks_work_data(trimesh);
//dummy contacts
GDYNAMIC_ARRAY dummycontacts;
GIM_CREATE_CONTACT_LIST(dummycontacts);
int cresult;
unsigned int i;
GUINT * boxesresult = GIM_DYNARRAY_POINTER(GUINT,collision_result);
GIM_TRIANGLE_CONTACT_DATA tri_contact_data;
GIM_TRIANGLE_DATA tri_data;
for(i=0;i<collision_result.m_size;i++)
{
gim_trimesh_get_triangle_data(trimesh,boxesresult[i],&tri_data);
cresult = gim_triangle_sphere_collision(&tri_data,center,radius,&tri_contact_data);
if(cresult!=0)
{
GIM_PUSH_CONTACT(dummycontacts, tri_contact_data.m_points[0],tri_contact_data.m_separating_normal ,tri_contact_data.m_penetration_depth,trimesh, 0, boxesresult[i],0);
}
}
///unlocks
gim_trimesh_unlocks_work_data(trimesh);
///Destroy box result
GIM_DYNARRAY_DESTROY(collision_result);
//merge contacts
gim_merge_contacts(&dummycontacts,contacts);
//Destroy dummy
GIM_DYNARRAY_DESTROY(dummycontacts);
}

View File

@ -0,0 +1,348 @@
/*
-----------------------------------------------------------------------------
This source file is part of GIMPACT Library.
For the latest info, see http://gimpact.sourceforge.net/
Copyright (c) 2006 Francisco Leon. 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.
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 and GIMPACT-LICENSE-BSD.TXT for more details.
-----------------------------------------------------------------------------
*/
#include "GIMPACT/gim_trimesh.h"
#define CLASSIFY_TRI_BY_FACE(v1,v2,v3,faceplane,out_of_face)\
{ \
_distances[0] = DISTANCE_PLANE_POINT(faceplane,v1);\
_distances[1] = _distances[0] * DISTANCE_PLANE_POINT(faceplane,v2);\
_distances[2] = _distances[0] * DISTANCE_PLANE_POINT(faceplane,v3); \
if(_distances[1]>0.0f && _distances[2]>0.0f)\
{\
out_of_face = 1;\
}\
else\
{\
out_of_face = 0;\
}\
}\
//! Receives the 3 edge planes
#define MOST_DEEP_POINTS(plane,points,point_count,deep_points,deep_points_count,maxdeep)\
{\
maxdeep=-1000.0f;\
GUINT _k;\
GREAL _dist;\
deep_points_count = 0;\
for(_k=0;_k<point_count;_k++)\
{\
_dist = -DISTANCE_PLANE_POINT(plane,points[_k]);\
if(_dist>maxdeep)\
{\
maxdeep = _dist;\
_max_candidates[0] = _k;\
deep_points_count=1;\
}\
else if((_dist+G_EPSILON)>=maxdeep)\
{\
_max_candidates[deep_points_count] = _k;\
deep_points_count++;\
}\
}\
if(maxdeep<0.0f)\
{\
deep_points_count = 0;\
}\
else\
{\
for(_k=0;_k<deep_points_count;_k++)\
{\
VEC_COPY(deep_points[_k],points[_max_candidates[_k]]);\
}\
}\
}\
//! Receives the 3 edge planes
#define CLIP_TRI_POINTS_BY_TRI_EDGE_PLANES(tri_points,tri_edge_planes, clipped_points, clipped_point_count)\
{\
clipped_point_count = 0; \
_temp_clip_count = 0;\
PLANE_CLIP_POLYGON(tri_edge_planes[0],tri_points,3,_temp_clip,_temp_clip_count,MAX_TRI_CLIPPING);\
if(_temp_clip_count>0)\
{\
_temp_clip_count2 = 0;\
PLANE_CLIP_POLYGON(tri_edge_planes[1],_temp_clip,_temp_clip_count,_temp_clip2,_temp_clip_count2,MAX_TRI_CLIPPING);\
if(_temp_clip_count2>0)\
{\
PLANE_CLIP_POLYGON(tri_edge_planes[2],_temp_clip2,_temp_clip_count2,clipped_points,clipped_point_count,MAX_TRI_CLIPPING);\
}\
}\
}\
int _gim_triangle_triangle_collision(
GIM_TRIANGLE_DATA *tri1,
GIM_TRIANGLE_DATA *tri2,
GIM_TRIANGLE_CONTACT_DATA * contact_data)
{
//Cache variables for triangle intersection
GUINT _max_candidates[MAX_TRI_CLIPPING];
vec3f _temp_clip[MAX_TRI_CLIPPING];
GUINT _temp_clip_count = 0;
vec3f _temp_clip2[MAX_TRI_CLIPPING];
GUINT _temp_clip_count2 = 0;
vec3f clipped_points2[MAX_TRI_CLIPPING];
vec3f deep_points2[MAX_TRI_CLIPPING];
vec3f clipped_points1[MAX_TRI_CLIPPING];
vec3f deep_points1[MAX_TRI_CLIPPING];
//State variabnles
GUINT mostdir=0;
GUINT clipped2_count=0;
//Clip tri2 by tri1 edges
CLIP_TRI_POINTS_BY_TRI_EDGE_PLANES(tri2->m_vertices,(&tri1->m_planes.m_planes[1]), clipped_points2, clipped2_count);
if(clipped2_count == 0 )
{
return 0;//Reject
}
//find most deep interval face1
GUINT deep2_count=0;
GREAL maxdeep;
MOST_DEEP_POINTS((tri1->m_planes.m_planes[0]), clipped_points2, clipped2_count, deep_points2, deep2_count, maxdeep);
if(deep2_count==0)
{
// *perror = 0.0f;
return 0;//Reject
}
//Normal pointing to triangle1
VEC_SCALE(contact_data->m_separating_normal,-1.0f,(tri1->m_planes.m_planes[0]));
//Clip tri1 by tri2 edges
GUINT clipped1_count=0;
CLIP_TRI_POINTS_BY_TRI_EDGE_PLANES(tri1->m_vertices,(&tri2->m_planes.m_planes[1]), clipped_points1, clipped1_count);
if(clipped2_count == 0 )
{
// *perror = 0.0f;
return 0;//Reject
}
//find interval face2
GUINT deep1_count=0;
GREAL dist;
MOST_DEEP_POINTS((tri2->m_planes.m_planes[0]), clipped_points1, clipped1_count, deep_points1, deep1_count, dist);
if(deep1_count==0)
{
// *perror = 0.0f;
return 0;
}
if(dist<maxdeep)
{
maxdeep = dist;
mostdir = 1;
VEC_COPY(contact_data->m_separating_normal,(tri2->m_planes.m_planes[0]));
}
//set deep
contact_data->m_penetration_depth = maxdeep;
////check most dir for contacts
if(mostdir==0)
{
contact_data->m_point_count = deep2_count;
for(mostdir=0;mostdir<deep2_count;mostdir++)
{
VEC_COPY(contact_data->m_points[mostdir] ,deep_points2[mostdir]);
}
}
else
{
contact_data->m_point_count = deep1_count;
for(mostdir=0;mostdir<deep1_count;mostdir++)
{
VEC_COPY(contact_data->m_points[mostdir] ,deep_points1[mostdir]);
}
}
return 1;
}
//! Finds the contact points from a collision of two triangles
/*!
Returns the contact points, the penetration depth and the separating normal of the collision
between two triangles. The normal is pointing toward triangle 1 from triangle 2
*/
int gim_triangle_triangle_collision(
GIM_TRIANGLE_DATA *tri1,
GIM_TRIANGLE_DATA *tri2,
GIM_TRIANGLE_CONTACT_DATA * contact_data)
{
vec3f _distances;
char out_of_face=0;
CLASSIFY_TRI_BY_FACE(tri1->m_vertices[0],tri1->m_vertices[1],tri1->m_vertices[2],tri2->m_planes.m_planes[0],out_of_face);
if(out_of_face==1) return 0;
CLASSIFY_TRI_BY_FACE(tri2->m_vertices[0],tri2->m_vertices[1],tri2->m_vertices[2],tri1->m_planes.m_planes[0],out_of_face);
if(out_of_face==1) return 0;
return _gim_triangle_triangle_collision(tri1,tri2,contact_data);
}
//! Trimesh Trimesh Collisions
/*!
In each contact
<ul>
<li> m_handle1 points to trimesh1.
<li> m_handle2 points to trimesh2.
<li> m_feature1 Is a triangle index of trimesh1.
<li> m_feature2 Is a triangle index of trimesh2.
</ul>
\param trimesh1 Collider
\param trimesh2 Collidee
\param contacts A GIM_CONTACT array. Must be initialized
*/
void gim_trimesh_trimesh_collision(GIM_TRIMESH * trimesh1, GIM_TRIMESH * trimesh2, GDYNAMIC_ARRAY * contacts)
{
contacts->m_size = 0;
GDYNAMIC_ARRAY collision_pairs;
GIM_CREATE_PAIR_SET(collision_pairs)
gim_aabbset_bipartite_intersections(&trimesh1->m_aabbset,&trimesh2->m_aabbset,&collision_pairs);
if(collision_pairs.m_size==0)
{
GIM_DYNARRAY_DESTROY(collision_pairs);
return; //no collisioin
}
//Locks meshes
gim_trimesh_locks_work_data(trimesh1);
gim_trimesh_locks_work_data(trimesh2);
//pair pointer
GIM_PAIR *pairs = GIM_DYNARRAY_POINTER(GIM_PAIR,collision_pairs);
//dummy contacts
GDYNAMIC_ARRAY dummycontacts;
GIM_CREATE_CONTACT_LIST(dummycontacts);
//Auxiliary triangle data
GIM_TRIANGLE_CONTACT_DATA tri_contact_data;
GIM_TRIANGLE_DATA tri1data,tri2data;
GUINT i, ti1,ti2,ci;
int colresult;
for (i=0;i<collision_pairs.m_size; i++)
{
ti1 = pairs[i].m_index1;
ti2 = pairs[i].m_index2;
//Get triangles data
gim_trimesh_get_triangle_data(trimesh1,ti1,&tri1data);
gim_trimesh_get_triangle_data(trimesh2,ti2,&tri2data);
//collide triangles
colresult = gim_triangle_triangle_collision(&tri1data,&tri2data,&tri_contact_data);
if(colresult == 1)
{
//Add contacts
for (ci=0;ci<tri_contact_data.m_point_count ;ci++ )
{
GIM_PUSH_CONTACT(dummycontacts, tri_contact_data.m_points[ci],tri_contact_data.m_separating_normal ,tri_contact_data.m_penetration_depth,trimesh1, trimesh2, ti1, ti2);
}
}
}
if(dummycontacts.m_size == 0) //reject
{
GIM_DYNARRAY_DESTROY(dummycontacts);
GIM_DYNARRAY_DESTROY(collision_pairs);
return;
}
//merge contacts
gim_merge_contacts(&dummycontacts,contacts);
//Terminate
GIM_DYNARRAY_DESTROY(dummycontacts);
GIM_DYNARRAY_DESTROY(collision_pairs);
//Unlocks meshes
gim_trimesh_unlocks_work_data(trimesh1);
gim_trimesh_unlocks_work_data(trimesh2);
}
//! Trimesh Plane Collisions
/*!
\param trimesh
\param plane vec4f plane
\param contacts A vec4f array. Must be initialized (~100). Each element have the coordinate point in the first 3 elements, and vec4f[3] has the penetration depth.
*/
void gim_trimesh_plane_collision(GIM_TRIMESH * trimesh,vec4f plane, GDYNAMIC_ARRAY * contacts)
{
contacts->m_size = 0;
char classify;
PLANE_CLASSIFY_BOX(plane,trimesh->m_aabbset.m_global_bound,classify);
if(classify>1) return; // in front of plane
//Locks mesh
gim_trimesh_locks_work_data(trimesh);
//Get vertices
GUINT i, vertcount = trimesh->m_transformed_vertex_buffer.m_element_count;
vec3f * vertices = GIM_BUFFER_ARRAY_POINTER(vec3f,trimesh->m_transformed_vertex_buffer,0);
GREAL dist;
vec4f * result_contact;
for (i=0;i<vertcount;i++)
{
dist = DISTANCE_PLANE_POINT(plane,vertices[i]);
if(dist<=0.0f)
{
GIM_DYNARRAY_PUSH_EMPTY(vec4f,(*contacts));
result_contact = GIM_DYNARRAY_POINTER_LAST(vec4f,(*contacts));
VEC_COPY((*result_contact),vertices[i]);
(*result_contact)[3] = -dist;
}
}
gim_trimesh_unlocks_work_data(trimesh);
}

View File

@ -0,0 +1,39 @@
/*
-----------------------------------------------------------------------------
This source file is part of GIMPACT Library.
For the latest info, see http://gimpact.sourceforge.net/
Copyright (c) 2006 Francisco Leon. 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.
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 and GIMPACT-LICENSE-BSD.TXT for more details.
-----------------------------------------------------------------------------
*/
#include "GIMPACT/gimpact.h"
void gimpact_init()
{
gim_init_math();
gim_init_buffer_managers();
}
void gimpact_terminate()
{
gim_terminate_buffer_managers();
}

View File

@ -0,0 +1,118 @@
As of version 0.6, ODE has two new build systems, one for Visual Studio
and another for just about everything else.
1. Building with Visual Studio
2. Building with Autotools (Linux, OS X, etc.)
3. Building with Code::Blocks
4. Building with Something Else
1. BUILDING WITH VISUAL STUDIO (2002 and up)
If you downloaded the source code from Subversion you must first copy
the file build/config-default.h to include/ode/config.h. If you
downloaded a source code package from SourceForge this has already
been done for you.
The directory ode/build contains project files for all supported versions
of Visual Studio. Open the appropriate solution for your version, build,
and go!
Single-precision math is used by default. If you would like to switch to
doubles instead, edit ode/include/ode/config.h and replace
#define dSINGLE 1
with the line
#define dDOUBLE 1
and the rebuild everything.
Note that Visual Studio 6 is no longer supported; please upgrade to
Visual Studio 2005 C++ Express (it's free!).
2. BUILDING WITH AUTOTOOLS (Linux, OS X, etc.)
If you downloaded the source code from Subversion you must bootstrap the
process by running the command:
$ sh autogen.sh
If you downloaded a source code package from SourceForge this has
already been done for you. You may see some "underquoted definition"
warnings depending on your platform, these are (for now) harmless
warnings regarding scripts from other m4 installed packages.
Run the configure script to autodetect your build environment.
$ ./configure
By default this will build ODE as a static library with single-precision
math, trimesh support, and debug symbols enabled. You can modify these
defaults by passing additional parameters to configure. For a full list
of available options, type
$ ./configure --help
Some of the more popular options are
--enable-double-precision enable double-precision math
--with-trimesh=none disables the trimesh support
--with-trimesh=opcode use OPCODE for trimesh code
--with-trimesh=gimpact use GIMPACT for trimesh code
--enable-release builds an optimized library
--enabled-shared builds a shared library
Once configure has run successfully, build and install ODE:
$ make
$ make install
The latter command will also create an `ode-config` script which you can
use to pass cflags and ldflags to your projects. run `ode-config` from a
command prompt to find out how it works.
In addition the option `--with-arch=` allows the user to pass the -march
flag to GCC, in order to tune the library for a particular architecture.
The arguments for --with-arch are listed on this page for -mtune:
http://gcc.gnu.org/onlinedocs/gcc-3.4.1/gcc/i386-and-x86-64-Options.html#i386%20and%20x86-64%20Options
Note that the link points to posible values for Intel processors, but
other processors are also supported, check the page for your particular
processor to see what parameters can be passed to -march in your case.
3. Building with Code::Blocks
Because Code::Blocks supports so many different platforms, we do not
provide workspaces. Instead, use Premake (http://www.premake.sourceforge.net/)
to create a workspace tailored for your platform and project.
Download Premake and place it on your system path (or anywhere convenient).
Then create a workspace like so:
$ cd ode/build
$ premake --with-tests --target cb-gcc
To see a complete list of options:
$ cd ode/build
$ premake --help
4. Building with Something Else
ODE uses the Premake tool to provide support for several different toolsets.
Premake adds support for new toolsets on a regular basis, so yours might be
supported. Check the Premake website at http://premake.sourceforge.net/,
and then follow the directions for Code::Blocks above, substituting your
toolset target in place of `cb-gcc`.

View File

@ -0,0 +1,34 @@
This is the BSD-style license for the Open Dynamics Engine
----------------------------------------------------------
Open Dynamics Engine
Copyright (c) 2001-2007, Russell L. Smith.
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 names of ODE's copyright owner 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.

View File

@ -0,0 +1,502 @@
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.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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.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
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.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View File

@ -0,0 +1,37 @@
LIBTOOL_DEPS = @LIBTOOL_DEPS@
libtool: $(LIBTOOL_DEPS)
$(SHELL) ./config.status --recheck
if ENABLE_DEMOS
SUBDIRS = include drawstuff ode tests
else
SUBDIRS = include ode tests
endif
bin_SCRIPTS = ode-config
# Utility rule for making a release
release: dist dist-bz2 dist-zip
@echo Created release packages for ${PACKAGE}-${VERSION}.
# Rules for creating .tar.bz2 and .zip packages
dist-bz2: ${PACKAGE}-${VERSION}.tar.gz
gzip -dc ${PACKAGE}-${VERSION}.tar.gz | bzip2 > ${PACKAGE}-${VERSION}.tar.bz2
dist-zip: ${PACKAGE}-${VERSION}.tar.gz
tar -zxf ${PACKAGE}-${VERSION}.tar.gz && \
zip -r ${PACKAGE}-${VERSION}.zip ${PACKAGE}-${VERSION} && \
rm -rf ${PACKAGE}-${VERSION}
if USE_SONAME
install-exec-hook:
ln -s $(libdir)/@ODE_SONAME@.@ODE_REVISION@.@ODE_AGE@ \
$(libdir)/libode.so
ln -s $(libdir)/@ODE_SONAME@.@ODE_REVISION@.@ODE_AGE@ \
$(libdir)/@ODE_SONAME@
ln -s $(libdir)/@ODE_SONAME@.@ODE_REVISION@.@ODE_AGE@ \
$(libdir)/@ODE_SONAME@.@ODE_REVISION@
/sbin/ldconfig
else
install-exec-hook:
endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,665 @@
# Makefile.in generated by automake 1.10 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
# 2003, 2004, 2005, 2006 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@
VPATH = @srcdir@
pkgdatadir = $(datadir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
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 = :
build_triplet = @build@
host_triplet = @host@
target_triplet = @target@
subdir = .
DIST_COMMON = $(am__configure_deps) $(srcdir)/Makefile.am \
$(srcdir)/Makefile.in $(srcdir)/ode-config.in \
$(top_srcdir)/configure config.guess config.sub depcomp \
install-sh missing
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(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 config.status.lineno
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/include/ode/config.h
CONFIG_CLEAN_FILES = ode-config
am__installdirs = "$(DESTDIR)$(bindir)"
binSCRIPT_INSTALL = $(INSTALL_SCRIPT)
SCRIPTS = $(bin_SCRIPTS)
SOURCES =
DIST_SOURCES =
RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
html-recursive info-recursive install-data-recursive \
install-dvi-recursive install-exec-recursive \
install-html-recursive install-info-recursive \
install-pdf-recursive install-ps-recursive install-recursive \
installcheck-recursive installdirs-recursive pdf-recursive \
ps-recursive uninstall-recursive
RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
distclean-recursive maintainer-clean-recursive
ETAGS = etags
CTAGS = ctags
DIST_SUBDIRS = include ode tests drawstuff
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@
ALLOCA = @ALLOCA@
AMTAR = @AMTAR@
ARCHFLAGS = @ARCHFLAGS@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CXX = @CXX@
CXXDEPMODE = @CXXDEPMODE@
CXXFLAGS = @CXXFLAGS@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DRAWSTUFF = @DRAWSTUFF@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
EXEEXT = @EXEEXT@
GL_LIBS = @GL_LIBS@
GREP = @GREP@
INSTALL = @INSTALL@
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@
MKDIR_P = @MKDIR_P@
OBJEXT = @OBJEXT@
ODE_AGE = @ODE_AGE@
ODE_CURRENT = @ODE_CURRENT@
ODE_RELEASE = @ODE_RELEASE@
ODE_REVISION = @ODE_REVISION@
ODE_SONAME = @ODE_SONAME@
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@
RANLIB = @RANLIB@
SET_MAKE = @SET_MAKE@
SHARED_LDFLAGS = @SHARED_LDFLAGS@
SHELL = @SHELL@
STRIP = @STRIP@
TOPDIR = @TOPDIR@
VERSION = @VERSION@
WINDRES = @WINDRES@
XMKMF = @XMKMF@
X_CFLAGS = @X_CFLAGS@
X_EXTRA_LIBS = @X_EXTRA_LIBS@
X_LIBS = @X_LIBS@
X_PRE_LIBS = @X_PRE_LIBS@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_CC = @ac_ct_CC@
ac_ct_CXX = @ac_ct_CXX@
ac_ct_WINDRES = @ac_ct_WINDRES@
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 = @build@
build_alias = @build_alias@
build_cpu = @build_cpu@
build_os = @build_os@
build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
host = @host@
host_alias = @host_alias@
host_cpu = @host_cpu@
host_os = @host_os@
host_vendor = @host_vendor@
htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
libdir = @libdir@
libexecdir = @libexecdir@
localedir = @localedir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
so_ext = @so_ext@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
target = @target@
target_alias = @target_alias@
target_cpu = @target_cpu@
target_os = @target_os@
target_vendor = @target_vendor@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
LIBTOOL_DEPS = @LIBTOOL_DEPS@
@ENABLE_DEMOS_FALSE@SUBDIRS = include ode tests
@ENABLE_DEMOS_TRUE@SUBDIRS = include drawstuff ode tests
bin_SCRIPTS = ode-config
all: all-recursive
.SUFFIXES:
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)
ode-config: $(top_builddir)/config.status $(srcdir)/ode-config.in
cd $(top_builddir) && $(SHELL) ./config.status $@
install-binSCRIPTS: $(bin_SCRIPTS)
@$(NORMAL_INSTALL)
test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
@list='$(bin_SCRIPTS)'; for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
if test -f $$d$$p; then \
f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
echo " $(binSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(bindir)/$$f'"; \
$(binSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(bindir)/$$f"; \
else :; fi; \
done
uninstall-binSCRIPTS:
@$(NORMAL_UNINSTALL)
@list='$(bin_SCRIPTS)'; for p in $$list; do \
f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
rm -f "$(DESTDIR)$(bindir)/$$f"; \
done
# This directory's subdirectories are mostly independent; you can cd
# into them and run `make' without going through this Makefile.
# To change the values of `make' variables: instead of editing Makefiles,
# (1) if the variable is set in `config.status', edit `config.status'
# (which will cause the Makefiles to be regenerated when you run `make');
# (2) otherwise, pass the desired values on the `make' command line.
$(RECURSIVE_TARGETS):
@failcom='exit 1'; \
for f in x $$MAKEFLAGS; do \
case $$f in \
*=* | --[!k]*);; \
*k*) failcom='fail=yes';; \
esac; \
done; \
dot_seen=no; \
target=`echo $@ | sed s/-recursive//`; \
list='$(SUBDIRS)'; for subdir in $$list; do \
echo "Making $$target in $$subdir"; \
if test "$$subdir" = "."; then \
dot_seen=yes; \
local_target="$$target-am"; \
else \
local_target="$$target"; \
fi; \
(cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
|| eval $$failcom; \
done; \
if test "$$dot_seen" = "no"; then \
$(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
fi; test -z "$$fail"
$(RECURSIVE_CLEAN_TARGETS):
@failcom='exit 1'; \
for f in x $$MAKEFLAGS; do \
case $$f in \
*=* | --[!k]*);; \
*k*) failcom='fail=yes';; \
esac; \
done; \
dot_seen=no; \
case "$@" in \
distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
*) list='$(SUBDIRS)' ;; \
esac; \
rev=''; for subdir in $$list; do \
if test "$$subdir" = "."; then :; else \
rev="$$subdir $$rev"; \
fi; \
done; \
rev="$$rev ."; \
target=`echo $@ | sed s/-recursive//`; \
for subdir in $$rev; do \
echo "Making $$target in $$subdir"; \
if test "$$subdir" = "."; then \
local_target="$$target-am"; \
else \
local_target="$$target"; \
fi; \
(cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
|| eval $$failcom; \
done && test -z "$$fail"
tags-recursive:
list='$(SUBDIRS)'; for subdir in $$list; do \
test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
done
ctags-recursive:
list='$(SUBDIRS)'; for subdir in $$list; do \
test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
done
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: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$(TAGS_FILES) $(LISP)
tags=; \
here=`pwd`; \
if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
include_option=--etags-include; \
empty_fix=.; \
else \
include_option=--include; \
empty_fix=; \
fi; \
list='$(SUBDIRS)'; for subdir in $$list; do \
if test "$$subdir" = .; then :; else \
test ! -f $$subdir/TAGS || \
tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
fi; \
done; \
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: ctags-recursive $(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)
test -d $(distdir) || mkdir $(distdir)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
dist_files=`for file in $$list; do echo $$file; done | \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
case $$dist_files in \
*/*) $(MKDIR_P) `echo "$$dist_files" | \
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
sort -u` ;; \
esac; \
for file in $$dist_files; do \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
if test -d $$d/$$file; then \
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
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
list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
if test "$$subdir" = .; then :; else \
test -d "$(distdir)/$$subdir" \
|| $(MKDIR_P) "$(distdir)/$$subdir" \
|| exit 1; \
distdir=`$(am__cd) $(distdir) && pwd`; \
top_distdir=`$(am__cd) $(top_distdir) && pwd`; \
(cd $$subdir && \
$(MAKE) $(AM_MAKEFLAGS) \
top_distdir="$$top_distdir" \
distdir="$$distdir/$$subdir" \
am__remove_distdir=: \
am__skip_length_check=: \
distdir) \
|| 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 $(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 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 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$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-recursive
all-am: Makefile $(SCRIPTS)
installdirs: installdirs-recursive
installdirs-am:
for dir in "$(DESTDIR)$(bindir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: install-recursive
install-exec: install-exec-recursive
install-data: install-data-recursive
uninstall: uninstall-recursive
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-recursive
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-recursive
clean-am: clean-generic mostlyclean-am
distclean: distclean-recursive
-rm -f $(am__CONFIG_DISTCLEAN_FILES)
-rm -f Makefile
distclean-am: clean-am distclean-generic distclean-tags
dvi: dvi-recursive
dvi-am:
html: html-recursive
info: info-recursive
info-am:
install-data-am:
install-dvi: install-dvi-recursive
install-exec-am: install-binSCRIPTS
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) install-exec-hook
install-html: install-html-recursive
install-info: install-info-recursive
install-man:
install-pdf: install-pdf-recursive
install-ps: install-ps-recursive
installcheck-am:
maintainer-clean: maintainer-clean-recursive
-rm -f $(am__CONFIG_DISTCLEAN_FILES)
-rm -rf $(top_srcdir)/autom4te.cache
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-recursive
mostlyclean-am: mostlyclean-generic
pdf: pdf-recursive
pdf-am:
ps: ps-recursive
ps-am:
uninstall-am: uninstall-binSCRIPTS
.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) install-am \
install-exec-am install-strip
.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
all all-am am--refresh check check-am clean clean-generic \
ctags ctags-recursive dist dist-all dist-bzip2 dist-gzip \
dist-shar dist-tarZ dist-zip distcheck distclean \
distclean-generic distclean-tags distcleancheck distdir \
distuninstallcheck dvi dvi-am html html-am info info-am \
install install-am install-binSCRIPTS install-data \
install-data-am install-dvi install-dvi-am install-exec \
install-exec-am install-exec-hook install-html install-html-am \
install-info install-info-am install-man install-pdf \
install-pdf-am install-ps install-ps-am install-strip \
installcheck installcheck-am installdirs installdirs-am \
maintainer-clean maintainer-clean-generic mostlyclean \
mostlyclean-generic pdf pdf-am ps ps-am tags tags-recursive \
uninstall uninstall-am uninstall-binSCRIPTS
libtool: $(LIBTOOL_DEPS)
$(SHELL) ./config.status --recheck
# Utility rule for making a release
release: dist dist-bz2 dist-zip
@echo Created release packages for ${PACKAGE}-${VERSION}.
# Rules for creating .tar.bz2 and .zip packages
dist-bz2: ${PACKAGE}-${VERSION}.tar.gz
gzip -dc ${PACKAGE}-${VERSION}.tar.gz | bzip2 > ${PACKAGE}-${VERSION}.tar.bz2
dist-zip: ${PACKAGE}-${VERSION}.tar.gz
tar -zxf ${PACKAGE}-${VERSION}.tar.gz && \
zip -r ${PACKAGE}-${VERSION}.zip ${PACKAGE}-${VERSION} && \
rm -rf ${PACKAGE}-${VERSION}
@USE_SONAME_TRUE@install-exec-hook:
@USE_SONAME_TRUE@ ln -s $(libdir)/@ODE_SONAME@.@ODE_REVISION@.@ODE_AGE@ \
@USE_SONAME_TRUE@ $(libdir)/libode.so
@USE_SONAME_TRUE@ ln -s $(libdir)/@ODE_SONAME@.@ODE_REVISION@.@ODE_AGE@ \
@USE_SONAME_TRUE@ $(libdir)/@ODE_SONAME@
@USE_SONAME_TRUE@ ln -s $(libdir)/@ODE_SONAME@.@ODE_REVISION@.@ODE_AGE@ \
@USE_SONAME_TRUE@ $(libdir)/@ODE_SONAME@.@ODE_REVISION@
@USE_SONAME_TRUE@ /sbin/ldconfig
@USE_SONAME_FALSE@install-exec-hook:
# 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:

View File

@ -0,0 +1,405 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains AABB-related code.
* \file IceAABB.cpp
* \author Pierre Terdiman
* \date January, 29, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* AABB class.
* \class AABB
* \author Pierre Terdiman
* \version 1.0
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.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)<GetMax(0)) return false;
if(box.GetMax(1)<GetMax(1)) return false;
if(box.GetMax(2)<GetMax(2)) return false;
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Computes the AABB planes.
* \param planes [out] 6 planes surrounding the box
* \return true if success
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool AABB::ComputePlanes(Plane* planes) const
{
// Checkings
if(!planes) return false;
Point Center, Extents;
GetCenter(Center);
GetExtents(Extents);
// Writes normals
planes[0].n = Point(1.0f, 0.0f, 0.0f);
planes[1].n = Point(-1.0f, 0.0f, 0.0f);
planes[2].n = Point(0.0f, 1.0f, 0.0f);
planes[3].n = Point(0.0f, -1.0f, 0.0f);
planes[4].n = Point(0.0f, 0.0f, 1.0f);
planes[5].n = Point(0.0f, 0.0f, -1.0f);
// Compute a point on each plane
Point p0 = Point(Center.x+Extents.x, Center.y, Center.z);
Point p1 = Point(Center.x-Extents.x, Center.y, Center.z);
Point p2 = Point(Center.x, Center.y+Extents.y, Center.z);
Point p3 = Point(Center.x, Center.y-Extents.y, Center.z);
Point p4 = Point(Center.x, Center.y, Center.z+Extents.z);
Point p5 = Point(Center.x, Center.y, Center.z-Extents.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 aabb points.
* \param pts [out] 8 box points
* \return true if success
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool AABB::ComputePoints(Point* pts) const
{
// Checkings
if(!pts) return false;
// Get box corners
Point min; GetMin(min);
Point max; GetMax(max);
// 7+------+6 0 = ---
// /| /| 1 = +--
// / | / | 2 = ++-
// / 4+---/--+5 3 = -+-
// 3+------+2 / y z 4 = --+
// | / | / | / 5 = +-+
// |/ |/ |/ 6 = +++
// 0+------+1 *---x 7 = -++
// Generate 8 corners of the bbox
pts[0] = Point(min.x, min.y, min.z);
pts[1] = Point(max.x, min.y, min.z);
pts[2] = Point(max.x, max.y, min.z);
pts[3] = Point(min.x, max.y, min.z);
pts[4] = Point(min.x, min.y, max.z);
pts[5] = Point(max.x, min.y, max.z);
pts[6] = Point(max.x, max.y, max.z);
pts[7] = Point(min.x, max.y, max.z);
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Gets vertex normals.
* \param pts [out] 8 box points
* \return true if success
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const Point* AABB::GetVertexNormals() 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
};
return (const Point*)VertexNormals;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Returns edges.
* \return 24 indices (12 edges) indexing the list returned by ComputePoints()
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const udword* AABB::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 edge normals.
* \return edge normals in local space
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const Point* AABB::GetEdgeNormals() 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;
}
// ===========================================================================
// (C) 1996-98 Vienna University of Technology
// ===========================================================================
// NAME: bboxarea
// TYPE: c++ code
// PROJECT: Bounding Box Area
// CONTENT: Computes area of 2D projection of 3D oriented bounding box
// VERSION: 1.0
// ===========================================================================
// AUTHORS: ds Dieter Schmalstieg
// ep Erik Pojar
// ===========================================================================
// HISTORY:
//
// 19-sep-99 15:23:03 ds last modification
// 01-dec-98 15:23:03 ep created
// ===========================================================================
//----------------------------------------------------------------------------
// SAMPLE CODE STARTS HERE
//----------------------------------------------------------------------------
// NOTE: This sample program requires OPEN INVENTOR!
//indexlist: this table stores the 64 possible cases of classification of
//the eyepoint with respect to the 6 defining planes of the bbox (2^6=64)
//only 26 (3^3-1, where 1 is "inside" cube) of these cases are valid.
//the first 6 numbers in each row are the indices of the bbox vertices that
//form the outline of which we want to compute the area (counterclockwise
//ordering), the 7th entry means the number of vertices in the outline.
//there are 6 cases with a single face and and a 4-vertex outline, and
//20 cases with 2 or 3 faces and a 6-vertex outline. a value of 0 indicates
//an invalid case.
// Original list was made of 7 items, I added an 8th element:
// - to padd on a cache line
// - to repeat the first entry to avoid modulos
//
// I also replaced original ints with sbytes.
static const sbyte gIndexList[64][8] =
{
{-1,-1,-1,-1,-1,-1,-1, 0}, // 0 inside
{ 0, 4, 7, 3, 0,-1,-1, 4}, // 1 left
{ 1, 2, 6, 5, 1,-1,-1, 4}, // 2 right
{-1,-1,-1,-1,-1,-1,-1, 0}, // 3 -
{ 0, 1, 5, 4, 0,-1,-1, 4}, // 4 bottom
{ 0, 1, 5, 4, 7, 3, 0, 6}, // 5 bottom, left
{ 0, 1, 2, 6, 5, 4, 0, 6}, // 6 bottom, right
{-1,-1,-1,-1,-1,-1,-1, 0}, // 7 -
{ 2, 3, 7, 6, 2,-1,-1, 4}, // 8 top
{ 0, 4, 7, 6, 2, 3, 0, 6}, // 9 top, left
{ 1, 2, 3, 7, 6, 5, 1, 6}, //10 top, right
{-1,-1,-1,-1,-1,-1,-1, 0}, //11 -
{-1,-1,-1,-1,-1,-1,-1, 0}, //12 -
{-1,-1,-1,-1,-1,-1,-1, 0}, //13 -
{-1,-1,-1,-1,-1,-1,-1, 0}, //14 -
{-1,-1,-1,-1,-1,-1,-1, 0}, //15 -
{ 0, 3, 2, 1, 0,-1,-1, 4}, //16 front
{ 0, 4, 7, 3, 2, 1, 0, 6}, //17 front, left
{ 0, 3, 2, 6, 5, 1, 0, 6}, //18 front, right
{-1,-1,-1,-1,-1,-1,-1, 0}, //19 -
{ 0, 3, 2, 1, 5, 4, 0, 6}, //20 front, bottom
{ 1, 5, 4, 7, 3, 2, 1, 6}, //21 front, bottom, left
{ 0, 3, 2, 6, 5, 4, 0, 6}, //22 front, bottom, right
{-1,-1,-1,-1,-1,-1,-1, 0}, //23 -
{ 0, 3, 7, 6, 2, 1, 0, 6}, //24 front, top
{ 0, 4, 7, 6, 2, 1, 0, 6}, //25 front, top, left
{ 0, 3, 7, 6, 5, 1, 0, 6}, //26 front, top, right
{-1,-1,-1,-1,-1,-1,-1, 0}, //27 -
{-1,-1,-1,-1,-1,-1,-1, 0}, //28 -
{-1,-1,-1,-1,-1,-1,-1, 0}, //29 -
{-1,-1,-1,-1,-1,-1,-1, 0}, //30 -
{-1,-1,-1,-1,-1,-1,-1, 0}, //31 -
{ 4, 5, 6, 7, 4,-1,-1, 4}, //32 back
{ 0, 4, 5, 6, 7, 3, 0, 6}, //33 back, left
{ 1, 2, 6, 7, 4, 5, 1, 6}, //34 back, right
{-1,-1,-1,-1,-1,-1,-1, 0}, //35 -
{ 0, 1, 5, 6, 7, 4, 0, 6}, //36 back, bottom
{ 0, 1, 5, 6, 7, 3, 0, 6}, //37 back, bottom, left
{ 0, 1, 2, 6, 7, 4, 0, 6}, //38 back, bottom, right
{-1,-1,-1,-1,-1,-1,-1, 0}, //39 -
{ 2, 3, 7, 4, 5, 6, 2, 6}, //40 back, top
{ 0, 4, 5, 6, 2, 3, 0, 6}, //41 back, top, left
{ 1, 2, 3, 7, 4, 5, 1, 6}, //42 back, top, right
{-1,-1,-1,-1,-1,-1,-1, 0}, //43 invalid
{-1,-1,-1,-1,-1,-1,-1, 0}, //44 invalid
{-1,-1,-1,-1,-1,-1,-1, 0}, //45 invalid
{-1,-1,-1,-1,-1,-1,-1, 0}, //46 invalid
{-1,-1,-1,-1,-1,-1,-1, 0}, //47 invalid
{-1,-1,-1,-1,-1,-1,-1, 0}, //48 invalid
{-1,-1,-1,-1,-1,-1,-1, 0}, //49 invalid
{-1,-1,-1,-1,-1,-1,-1, 0}, //50 invalid
{-1,-1,-1,-1,-1,-1,-1, 0}, //51 invalid
{-1,-1,-1,-1,-1,-1,-1, 0}, //52 invalid
{-1,-1,-1,-1,-1,-1,-1, 0}, //53 invalid
{-1,-1,-1,-1,-1,-1,-1, 0}, //54 invalid
{-1,-1,-1,-1,-1,-1,-1, 0}, //55 invalid
{-1,-1,-1,-1,-1,-1,-1, 0}, //56 invalid
{-1,-1,-1,-1,-1,-1,-1, 0}, //57 invalid
{-1,-1,-1,-1,-1,-1,-1, 0}, //58 invalid
{-1,-1,-1,-1,-1,-1,-1, 0}, //59 invalid
{-1,-1,-1,-1,-1,-1,-1, 0}, //60 invalid
{-1,-1,-1,-1,-1,-1,-1, 0}, //61 invalid
{-1,-1,-1,-1,-1,-1,-1, 0}, //62 invalid
{-1,-1,-1,-1,-1,-1,-1, 0} //63 invalid
};
const sbyte* AABB::ComputeOutline(const Point& local_eye, sdword& num) const
{
// Get box corners
Point min; GetMin(min);
Point max; GetMax(max);
// Compute 6-bit code to classify eye with respect to the 6 defining planes of the bbox
int pos = ((local_eye.x < min.x) ? 1 : 0) // 1 = left
+ ((local_eye.x > 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<num;i++)
{
HPoint Projected;
vertexBox[Outline[i]].ProjectToScreen(width, height, mat, Projected);
dst[i] = Projected;
}
float Sum = (dst[num-1][0] - dst[0][0]) * (dst[num-1][1] + dst[0][1]);
for(int i=0; i<num-1; i++)
Sum += (dst[i][0] - dst[i+1][0]) * (dst[i][1] + dst[i+1][1]);
return Sum * 0.5f; //return computed value corrected by 0.5
}

View File

@ -0,0 +1,505 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains AABB-related code. (axis-aligned bounding box)
* \file IceAABB.h
* \author Pierre Terdiman
* \date January, 13, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __ICEAABB_H__
#define __ICEAABB_H__
// Forward declarations
class Sphere;
//! Declarations of type-independent methods (most of them implemented in the .cpp)
#define AABB_COMMON_METHODS \
AABB& Add(const AABB& aabb); \
float MakeCube(AABB& cube) const; \
void MakeSphere(Sphere& sphere) const; \
const sbyte* ComputeOutline(const Point& local_eye, sdword& num) const; \
float ComputeBoxArea(const Point& eye, const Matrix4x4& mat, float width, float height, sdword& num) const; \
bool IsInside(const AABB& box) const; \
bool ComputePlanes(Plane* planes) const; \
bool ComputePoints(Point* pts) const; \
const Point* GetVertexNormals() const; \
const udword* GetEdges() const; \
const Point* GetEdgeNormals() const; \
inline_ BOOL ContainsPoint(const Point& p) const \
{ \
if(p.x > 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__

View File

@ -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__

View File

@ -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__

View File

@ -0,0 +1,345 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 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 Container
* \author Pierre Terdiman
* \version 1.0
* \date 08.15.98
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.h"
using namespace IceCore;
// Static members
#ifdef CONTAINER_STATS
udword Container::mNbContainers = 0;
udword Container::mUsedRam = 0;
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Constructor. No entries allocated there.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Container::Container() : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(2.0f)
{
#ifdef CONTAINER_STATS
mNbContainers++;
mUsedRam+=sizeof(Container);
#endif
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Constructor. Also allocates a given number of entries.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Container::Container(udword size, float growth_factor) : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(growth_factor)
{
#ifdef CONTAINER_STATS
mNbContainers++;
mUsedRam+=sizeof(Container);
#endif
SetSize(size);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Copy constructor.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Container::Container(const Container& object) : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(2.0f)
{
#ifdef CONTAINER_STATS
mNbContainers++;
mUsedRam+=sizeof(Container);
#endif
*this = object;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Destructor. Frees everything and leaves.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Container::~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
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Container& 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 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(mMaxNbEntries<mCurNbEntries + needed) mMaxNbEntries = mCurNbEntries + needed;
// Get some bytes for new entries
udword* NewEntries = new udword[mMaxNbEntries];
CHECKALLOC(NewEntries);
#ifdef CONTAINER_STATS
// Add current amount of bytes
mUsedRam+=mMaxNbEntries*sizeof(udword);
#endif
// Copy old data if needed
if(mCurNbEntries) CopyMemory(NewEntries, mEntries, mCurNbEntries*sizeof(udword));
// Delete old data
DELETEARRAY(mEntries);
// Assign new pointer
mEntries = NewEntries;
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 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 Container::SetSize(udword nb)
{
// Make sure it's empty
Empty();
// Checkings
if(!nb) return false;
// Initialize for nb entries
mMaxNbEntries = nb;
// Get some bytes for new entries
mEntries = new udword[mMaxNbEntries];
CHECKALLOC(mEntries);
#ifdef CONTAINER_STATS
// Add current amount of bytes
mUsedRam+=mMaxNbEntries*sizeof(udword);
#endif
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Refits the container and get rid of unused bytes.
* \return true if success
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Container::Refit()
{
#ifdef CONTAINER_STATS
// Subtract previous amount of bytes
mUsedRam-=mMaxNbEntries*sizeof(udword);
#endif
// Get just enough entries
mMaxNbEntries = mCurNbEntries;
if(!mMaxNbEntries) return false;
// Get just enough bytes
udword* NewEntries = new udword[mMaxNbEntries];
CHECKALLOC(NewEntries);
#ifdef CONTAINER_STATS
// Add current amount of bytes
mUsedRam+=mMaxNbEntries*sizeof(udword);
#endif
// Copy old data
CopyMemory(NewEntries, mEntries, mCurNbEntries*sizeof(udword));
// Delete old data
DELETEARRAY(mEntries);
// Assign new pointer
mEntries = NewEntries;
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Checks whether the container already contains a given value.
* \param entry [in] the value to look for in the container
* \param location [out] a possible pointer to store the entry location
* \see Add(udword entry)
* \see Add(float entry)
* \see Empty()
* \return true if the value has been found in the container, else false.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Container::Contains(udword entry, udword* location) const
{
// Look for the entry
for(udword i=0;i<mCurNbEntries;i++)
{
if(mEntries[i]==entry)
{
if(location) *location = i;
return true;
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Deletes an entry. If the container contains such an entry, it's removed.
* \param entry [in] the value to delete.
* \return true if the value has been found in the container, else false.
* \warning This method is arbitrary slow (O(n)) and should be used carefully. Insertion order is not preserved.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Container::Delete(udword entry)
{
// Look for the entry
for(udword i=0;i<mCurNbEntries;i++)
{
if(mEntries[i]==entry)
{
// Entry has been found at index i. The strategy is to copy the last current entry at index i, and decrement the current number of entries.
DeleteIndex(i);
return true;
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Deletes an entry, preserving the insertion order. If the container contains such an entry, it's removed.
* \param entry [in] the value to delete.
* \return true if the value has been found in the container, else false.
* \warning This method is arbitrary slow (O(n)) and should be used carefully.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Container::DeleteKeepingOrder(udword entry)
{
// Look for the entry
for(udword i=0;i<mCurNbEntries;i++)
{
if(mEntries[i]==entry)
{
// Entry has been found at index i.
// Shift entries to preserve order. You really should use a linked list instead.
mCurNbEntries--;
for(udword j=i;j<mCurNbEntries;j++)
{
mEntries[j] = mEntries[j+1];
}
return true;
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Gets the next entry, starting from input one.
* \param entry [in/out] On input, the entry to look for. On output, the next entry
* \param find_mode [in] wrap/clamp
* \return Self-Reference
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Container& Container::FindNext(udword& entry, FindMode find_mode)
{
udword Location;
if(Contains(entry, &Location))
{
Location++;
if(Location==mCurNbEntries) Location = find_mode==FIND_WRAP ? 0 : mCurNbEntries-1;
entry = mEntries[Location];
}
return *this;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Gets the previous entry, starting from input one.
* \param entry [in/out] On input, the entry to look for. On output, the previous entry
* \param find_mode [in] wrap/clamp
* \return Self-Reference
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Container& Container::FindPrev(udword& entry, FindMode find_mode)
{
udword Location;
if(Contains(entry, &Location))
{
Location--;
if(Location==0xffffffff) Location = find_mode==FIND_WRAP ? mCurNbEntries-1 : 0;
entry = mEntries[Location];
}
return *this;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Gets the ram used by the container.
* \return the ram used in bytes.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
udword Container::GetUsedRam() const
{
return sizeof(Container) + mMaxNbEntries * sizeof(udword);
}
/*void Container::operator=(const Container& object)
{
SetSize(object.GetNbEntries());
CopyMemory(mEntries, object.GetEntries(), mMaxNbEntries*sizeof(udword));
mCurNbEntries = mMaxNbEntries;
}*/

View File

@ -0,0 +1,212 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains a simple container class.
* \file IceContainer.h
* \author Pierre Terdiman
* \date February, 5, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __ICECONTAINER_H__
#define __ICECONTAINER_H__
#define CONTAINER_STATS
enum FindMode
{
FIND_CLAMP,
FIND_WRAP,
FIND_FORCE_DWORD = 0x7fffffff
};
class ICECORE_API Container
{
public:
// Constructor / Destructor
Container();
Container(const Container& object);
Container(udword size, float growth_factor);
~Container();
// Management
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 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 udword to store in the container
* \see Add(float entry)
* \see Empty()
* \see Contains(udword entry)
* \return Self-Reference
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
inline_ Container& Add(udword entry)
{
// Resize if needed
if(mCurNbEntries==mMaxNbEntries) Resize();
// Add new entry
mEntries[mCurNbEntries++] = entry;
return *this;
}
inline_ Container& Add(const udword* entries, udword nb)
{
// Resize if needed
if(mCurNbEntries+nb>mMaxNbEntries) 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_ Container& Add(float entry)
{
// Resize if needed
if(mCurNbEntries==mMaxNbEntries) Resize();
// Add new entry
mEntries[mCurNbEntries++] = IR(entry);
return *this;
}
inline_ 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_ 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
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
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
Container& FindNext(udword& entry, FindMode find_mode=FIND_CLAMP);
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<mCurNbEntries); return mEntries[i]; }
//! Write-access as an array
inline_ udword& operator[](udword i) { ASSERT(i>=0 && i<mCurNbEntries); return mEntries[i]; }
// Stats
udword GetUsedRam() const;
//! Operator for "Container A = Container B"
//void operator = (const Container& object);
#ifdef CONTAINER_STATS
inline_ udword GetNbContainers() const { return mNbContainers; }
inline_ udword GetTotalBytes() const { return mUsedRam; }
private:
static udword mNbContainers; //!< Number of containers around
static udword mUsedRam; //!< Amount of bytes used by containers in the system
#endif
private:
// Resizing
bool Resize(udword needed=1);
// Data
udword mMaxNbEntries; //!< Maximum possible number of entries
udword mCurNbEntries; //!< Current number of entries
udword* mEntries; //!< List of entries
float mGrowthFactor; //!< Resize: new number of entries = old number * mGrowthFactor
};
#endif // __ICECONTAINER_H__

View File

@ -0,0 +1,337 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains FPU related code.
* \file IceFPU.h
* \author Pierre Terdiman
* \date April, 4, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __ICEFPU_H__
#define __ICEFPU_H__
#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)
{
#ifdef _MSC_VER
float retval;
__asm {
mov eax, square
sub eax, 0x3F800000
sar eax, 1
add eax, 0x3F800000
mov [retval], eax
}
return retval;
#else
return sqrt(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;
}
#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)
{
#ifdef _MSC_VER
float Res;
_asm fld [a]
_asm fld [b]
FCOMI_ST1
FCMOVB_ST1
_asm fstp [Res]
_asm fcomp
return Res;
#else
return (a > b) ? a : b;
#endif
}
//! A global function to find MIN(a,b) using FCOMI/FCMOV
inline_ float FCMin2(float a, float b)
{
#ifdef _MSC_VER
float Res;
_asm fld [a]
_asm fld [b]
FCOMI_ST1
FCMOVNB_ST1
_asm fstp [Res]
_asm fcomp
return Res;
#else
return (a < b) ? a : b;
#endif
}
//! A global function to find MAX(a,b,c) using FCOMI/FCMOV
inline_ float FCMax3(float a, float b, float c)
{
#ifdef _MSC_VER
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;
#else
return (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c);
#endif
}
//! A global function to find MIN(a,b,c) using FCOMI/FCMOV
inline_ float FCMin3(float a, float b, float c)
{
#ifdef _MSC_VER
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;
#else
return (a < b) ? ((a < c) ? a : c) : ((b < c) ? b : 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__

View File

@ -0,0 +1,70 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 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
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.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;
}

View File

@ -0,0 +1,160 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 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 xx, float yy, float zz, float ww=0.0f) : Point(xx, yy, zz), w(ww) {}
//! Constructor from array
inline_ HPoint(const float f[4]) : Point(f), w(f[3]) {}
//! Constructor from a Point
inline_ HPoint(const Point& p, float ww=0.0f) : Point(p), w(ww) {}
//! Destructor
inline_ ~HPoint() {}
//! Clear the point
inline_ HPoint& Zero() { x = y = z = w = 0.0f; return *this; }
//! Assignment from values
inline_ HPoint& Set(float xx, float yy, float zz, float ww ) { x = xx; y = yy; z = zz; w = ww; 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 xx, float yy, float zz, float ww ) { x += xx; y += yy; z += zz; w += ww; 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 xx, float yy, float zz, float ww ) { x -= xx; y -= yy; z -= zz; w -= ww; 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.
#ifdef _MSC_VER
inline_ operator Point() const { return Point(x, y, z); }
// gcc complains that conversion to a base class will never use a type conversion operator
#endif
public:
float w;
};
#endif // __ICEHPOINT_H__

View File

@ -0,0 +1,548 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains a handy indexed triangle class.
* \file IceIndexedTriangle.cpp
* \author Pierre Terdiman
* \date January, 17, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.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 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;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 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]));
}

View File

@ -0,0 +1,72 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains a handy indexed triangle class.
* \file IceIndexedTriangle.h
* \author Pierre Terdiman
* \date January, 17, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __ICEINDEXEDTRIANGLE_H__
#define __ICEINDEXEDTRIANGLE_H__
// Forward declarations
#ifdef _MSC_VER
enum CubeIndex;
#else
typedef int CubeIndex;
#endif
// An indexed triangle class.
class ICEMATHS_API IndexedTriangle
{
public:
//! Constructor
inline_ IndexedTriangle() {}
//! Constructor
inline_ IndexedTriangle(udword r0, udword r1, udword r2) { mVRef[0]=r0; mVRef[1]=r1; mVRef[2]=r2; }
//! Copy constructor
inline_ IndexedTriangle(const IndexedTriangle& triangle)
{
mVRef[0] = triangle.mVRef[0];
mVRef[1] = triangle.mVRef[1];
mVRef[2] = triangle.mVRef[2];
}
//! Destructor
inline_ ~IndexedTriangle() {}
//! Vertex-references
udword mVRef[3];
// 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;
CubeIndex ComputeCubeIndex(const Point* verts) const;
};
#endif // __ICEINDEXEDTRIANGLE_H__

View File

@ -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__

View File

@ -0,0 +1,48 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 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
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.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);
}

View File

@ -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__

View File

@ -0,0 +1,135 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 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
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.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;
}

View File

@ -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__

View File

@ -0,0 +1,109 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 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)
{
// 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;i<nb;i++) dest[i] = value;
//
#ifdef _MSC_VER
_asm push eax
_asm push ecx
_asm push edi
_asm mov edi, dest
_asm mov ecx, nb
_asm mov eax, value
_asm rep stosd
_asm pop edi
_asm pop ecx
_asm pop eax
#else
while(nb--) *dest++ = value;
#endif
}
//! Copies a buffer.
//! \param addr [in] destination buffer address
//! \param addr [in] source buffer address
//! \param size [in] buffer length
//! \see ZeroMemory
//! \see FillMemory
//! \see StoreDwords
//! \see MoveMemory
inline_ void CopyMemory(void* dest, const void* src, udword size) { memcpy(dest, src, size); }
//! Moves a buffer.
//! \param addr [in] destination buffer address
//! \param addr [in] source buffer address
//! \param size [in] buffer length
//! \see ZeroMemory
//! \see FillMemory
//! \see StoreDwords
//! \see CopyMemory
inline_ void MoveMemory(void* dest, const void* src, udword size) { memmove(dest, src, size); }
#define SIZEOFOBJECT sizeof(*this) //!< Gives the size of current object. Avoid some mistakes (e.g. "sizeof(this)").
//#define CLEAROBJECT { memset(this, 0, SIZEOFOBJECT); } //!< Clears current object. Laziness is my business. HANDLE WITH CARE.
#define DELETESINGLE(x) if (x) { delete x; x = null; } //!< Deletes an instance of a class.
#define DELETEARRAY(x) if (x) { delete []x; x = null; } //!< Deletes an array.
#define SAFE_RELEASE(x) if (x) { (x)->Release(); (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__

View File

@ -0,0 +1,323 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 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
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.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;
}

View File

@ -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__

View File

@ -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 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__

View File

@ -0,0 +1,45 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains code for planes.
* \file IcePlane.cpp
* \author Pierre Terdiman
* \date April, 4, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Plane class.
* \class Plane
* \author Pierre Terdiman
* \version 1.0
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.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;
}

View File

@ -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__

View File

@ -0,0 +1,193 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 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
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.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;
}

View File

@ -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 xx, float yy, float zz) : x(xx), y(yy), z(zz) {}
//! 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 xx, float yy, float zz) { x = xx; y = yy; z = zz; 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 xx, float yy, float zz) { x += xx; y += yy; z += zz; 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 xx, float yy, float zz) { x -= xx; y -= yy; z -= zz; 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(x<min) x=min; if(x>max) x=max;
if(y<min) y=min; if(y>max) y=max;
if(z<min) z=min; if(z>max) 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__

View File

@ -0,0 +1,132 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 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 defined( _WIN32 ) || defined( WIN32 )
// #pragma message("Compiling on Windows...")
#define PLATFORM_WINDOWS
#else
// don't issue pragmas on unknown platforms
// #pragma message("Compiling on unknown platform...")
#endif
// Check compiler
#if defined(_MSC_VER)
// #pragma message("Compiling with VC++...")
#define COMPILER_VISUAL_CPP
#else
// don't issue pragmas on unknown platforms
// #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
#ifdef _MSC_VER
#pragma inline_depth( 255 )
#endif
#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__

View File

@ -0,0 +1,35 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains code for random generators.
* \file IceRandom.cpp
* \author Pierre Terdiman
* \date August, 9, 2001
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.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;
}

View File

@ -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__

View File

@ -0,0 +1,84 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 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
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.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();
}

View File

@ -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__

View File

@ -0,0 +1,520 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 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 ?
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.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(Val<PrevVal) { AlreadySorted = false; break; } /* Early out */ \
/* Update for next iteration */ \
PrevVal = Val; \
\
/* Create histograms */ \
h0[*p++]++; h1[*p++]++; h2[*p++]++; h3[*p++]++; \
} \
\
/* If all input values are already sorted, we just have to return and leave the */ \
/* previous list unchanged. That way the routine may take advantage of temporal */ \
/* coherence, for example when used to sort transparent faces. */ \
if(AlreadySorted) \
{ \
mNbHits++; \
for(udword i=0;i<nb;i++) mRanks[i] = i; \
return *this; \
} \
} \
else \
{ \
/* Prepare for temporal coherence */ \
udword* Indices = mRanks; \
type PrevVal = (type)buffer[*Indices]; \
\
while(p!=pe) \
{ \
/* Read input buffer in previous sorted order */ \
type Val = (type)buffer[*Indices++]; \
/* Check whether already sorted or not */ \
if(Val<PrevVal) { AlreadySorted = false; break; } /* Early out */ \
/* Update for next iteration */ \
PrevVal = Val; \
\
/* Create histograms */ \
h0[*p++]++; h1[*p++]++; h2[*p++]++; h3[*p++]++; \
} \
\
/* If all input values are already sorted, we just have to return and leave the */ \
/* previous list unchanged. That way the routine may take advantage of temporal */ \
/* coherence, for example when used to sort transparent faces. */ \
if(AlreadySorted) { mNbHits++; return *this; } \
} \
\
/* Else there has been an early out and we must finish computing the histograms */ \
while(p!=pe) \
{ \
/* Create histograms without the previous overhead */ \
h0[*p++]++; h1[*p++]++; h2[*p++]++; h3[*p++]++; \
}
#define CHECK_PASS_VALIDITY(pass) \
/* Shortcut to current counters */ \
udword* CurCount = &mHistogram[pass<<8]; \
\
/* Reset flag. The sorting pass is supposed to be performed. (default) */ \
bool PerformPass = true; \
\
/* Check pass validity */ \
\
/* If all values have the same byte, sorting is useless. */ \
/* It may happen when sorting bytes or words instead of dwords. */ \
/* This routine actually sorts words faster than dwords, and bytes */ \
/* faster than words. Standard running time (O(4*n))is reduced to O(2*n) */ \
/* for words and O(n) for bytes. Running time for floats depends on actual values... */ \
\
/* Get first byte */ \
ubyte UniqueVal = *(((ubyte*)input)+pass); \
\
/* Check that byte's counter */ \
if(CurCount[UniqueVal]==nb) PerformPass=false;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Constructor.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
RadixSort::RadixSort() : mRanks(null), mRanks2(null), mCurrentSize(0), mTotalCalls(0), mNbHits(0)
{
#ifndef RADIX_LOCAL_RAM
// Allocate input-independent ram
mHistogram = new udword[256*4];
mOffset = new udword[256];
#endif
// Initialize indices
INVALIDATE_RANKS;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Destructor.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
RadixSort::~RadixSort()
{
// Release everything
#ifndef RADIX_LOCAL_RAM
DELETEARRAY(mOffset);
DELETEARRAY(mHistogram);
#endif
DELETEARRAY(mRanks2);
DELETEARRAY(mRanks);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Resizes the inner lists.
* \param nb [in] new size (number of dwords)
* \return true if success
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool RadixSort::Resize(udword nb)
{
// Free previously used ram
DELETEARRAY(mRanks2);
DELETEARRAY(mRanks);
// Get some fresh one
mRanks = new udword[nb]; CHECKALLOC(mRanks);
mRanks2 = new udword[nb]; CHECKALLOC(mRanks2);
return true;
}
inline_ void RadixSort::CheckResize(udword nb)
{
udword CurSize = CURRENT_SIZE;
if(nb!=CurSize)
{
if(nb>CurSize) 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<nb;i++) mRanks2[mOffset[InputBytes[i<<2]]++] = i;
for(udword i=0;i<nb;i++) *mLink[InputBytes[i<<2]]++ = i;
VALIDATE_RANKS;
}
else
{
udword* Indices = mRanks;
udword* IndicesEnd = &mRanks[nb];
while(Indices!=IndicesEnd)
{
udword id = *Indices++;
// mRanks2[mOffset[InputBytes[id<<2]]++] = id;
*mLink[InputBytes[id<<2]]++ = id;
}
}
// Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap.
udword* Tmp = mRanks; mRanks = mRanks2; mRanks2 = Tmp;
}
}
return *this;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Main sort routine.
* This one is for floating-point 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 floating-point values to sort
* \param nb [in] number of values to sort, must be < 2^31
* \return Self-Reference
* \warning only sorts IEEE floating-point values
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
RadixSort& RadixSort::Sort(const float* input2, udword nb)
{
// Checkings
if(!input2 || !nb || nb&0x80000000) return *this;
// Stats
mTotalCalls++;
udword* input = (udword*)input2;
// 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
// Floating-point values are always supposed to be signed values, so there's only one code path there.
// Please note the floating point comparison needed for temporal coherence! Although the resulting asm code
// is dreadful, this is surprisingly not such a performance hit - well, I suppose that's a big one on first
// generation Pentiums....We can't make comparison on integer representations because, as Chris said, it just
// wouldn't work with mixed positive/negative values....
{ CREATE_HISTOGRAMS(float, input2); }
// Compute #negative values involved if needed
udword NbNegativeValues = 0;
// 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++)
{
// Should we care about negative values?
if(j!=3)
{
// Here we deal with positive values only
CHECK_PASS_VALIDITY(j);
if(PerformPass)
{
// Create offsets
// mOffset[0] = 0;
mLink[0] = mRanks2;
// for(udword i=1;i<256;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1];
for(udword i=1;i<256;i++) mLink[i] = mLink[i-1] + CurCount[i-1];
// Perform Radix Sort
ubyte* InputBytes = (ubyte*)input;
InputBytes += j;
if(INVALID_RANKS)
{
// for(i=0;i<nb;i++) mRanks2[mOffset[InputBytes[i<<2]]++] = i;
for(udword i=0;i<nb;i++) *mLink[InputBytes[i<<2]]++ = i;
VALIDATE_RANKS;
}
else
{
udword* Indices = mRanks;
udword* IndicesEnd = &mRanks[nb];
while(Indices!=IndicesEnd)
{
udword id = *Indices++;
// mRanks2[mOffset[InputBytes[id<<2]]++] = id;
*mLink[InputBytes[id<<2]]++ = id;
}
}
// 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
{
// This is a special case to correctly handle negative values
CHECK_PASS_VALIDITY(j);
if(PerformPass)
{
// 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
// We must reverse the sorting order for negative numbers!
// mOffset[255] = 0;
mLink[255] = mRanks2;
// for(i=0;i<127;i++) mOffset[254-i] = mOffset[255-i] + CurCount[255-i]; // Fixing the wrong order for negative values
for(udword i=0;i<127;i++) mLink[254-i] = mLink[255-i] + CurCount[255-i]; // Fixing the wrong order for negative values
// for(i=128;i<256;i++) mOffset[i] += CurCount[i]; // Fixing the wrong place for negative values
for(udword i=128;i<256;i++) mLink[i] += CurCount[i]; // Fixing the wrong place for negative values
// Perform Radix Sort
if(INVALID_RANKS)
{
for(udword i=0;i<nb;i++)
{
udword Radix = input[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<nb;i++)
{
udword Radix = input[mRanks[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<nb;i++) mRanks2[i] = nb-i-1;
VALIDATE_RANKS;
}
else
{
for(udword i=0;i<nb;i++) mRanks2[i] = mRanks[nb-i-1];
}
// Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap.
udword* Tmp = mRanks; mRanks = mRanks2; mRanks2 = Tmp;
}
}
}
}
return *this;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Gets the ram used.
* \return memory used in bytes
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
udword RadixSort::GetUsedRam() const
{
udword UsedRam = sizeof(RadixSort);
#ifndef RADIX_LOCAL_RAM
UsedRam += 256*4*sizeof(udword); // Histograms
UsedRam += 256*sizeof(udword); // Offsets
#endif
UsedRam += 2*CURRENT_SIZE*sizeof(udword); // 2 lists of indices
return UsedRam;
}

View File

@ -0,0 +1,65 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains source code from the article "Radix Sort Revisited".
* \file IceRevisitedRadix.h
* \author Pierre Terdiman
* \date April, 4, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __ICERADIXSORT_H__
#define __ICERADIXSORT_H__
//! Allocate histograms & offsets locally
#define RADIX_LOCAL_RAM
enum RadixHint
{
RADIX_SIGNED, //!< Input values are signed
RADIX_UNSIGNED, //!< Input values are unsigned
RADIX_FORCE_DWORD = 0x7fffffff
};
class ICECORE_API RadixSort
{
public:
// Constructor/Destructor
RadixSort();
~RadixSort();
// Sorting methods
RadixSort& Sort(const udword* input, udword nb, RadixHint hint=RADIX_SIGNED);
RadixSort& Sort(const float* input, udword nb);
//! Access to results. mRanks is a list of indices in sorted order, i.e. in the order you may further process your data
inline_ const udword* GetRanks() const { return mRanks; }
//! mIndices2 gets trashed on calling the sort routine, but otherwise you can recycle it the way you want.
inline_ udword* GetRecyclable() const { return mRanks2; }
// Stats
udword GetUsedRam() const;
//! Returns the total number of calls to the radix sorter.
inline_ udword GetNbTotalCalls() const { return mTotalCalls; }
//! Returns the number of eraly exits due to temporal coherence.
inline_ udword GetNbHits() const { return mNbHits; }
private:
#ifndef RADIX_LOCAL_RAM
udword* mHistogram; //!< Counters for each byte
udword* mOffset; //!< Offsets (nearly a cumulative distribution function)
#endif
udword mCurrentSize; //!< Current size of the indices list
udword* mRanks; //!< Two lists, swapped each pass
udword* mRanks2;
// Stats
udword mTotalCalls; //!< Total number of calls to the sort routine
udword mNbHits; //!< Number of early exits due to coherence
// Internal methods
void CheckResize(udword nb);
bool Resize(udword nb);
};
#endif // __ICERADIXSORT_H__

View File

@ -0,0 +1,57 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains code for segments.
* \file IceSegment.cpp
* \author Pierre Terdiman
* \date April, 4, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Segment class.
* A segment is defined by S(t) = mP0 * (1 - t) + mP1 * t, with 0 <= t <= 1
* Alternatively, a segment is S(t) = Origin + t * Direction for 0 <= t <= 1.
* Direction is not necessarily unit length. The end points are Origin = mP0 and Origin + Direction = mP1.
*
* \class Segment
* \author Pierre Terdiman
* \version 1.0
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.h"
using namespace IceMaths;
float Segment::SquareDistance(const Point& point, float* t) const
{
Point Diff = point - mP0;
Point Dir = mP1 - mP0;
float fT = Diff | Dir;
if(fT<=0.0f)
{
fT = 0.0f;
}
else
{
float SqrLen= Dir.SquareMagnitude();
if(fT>=SqrLen)
{
fT = 1.0f;
Diff -= Dir;
}
else
{
fT /= SqrLen;
Diff -= fT*Dir;
}
}
if(t) *t = fT;
return Diff.SquareMagnitude();
}

View File

@ -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__

View File

@ -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 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 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__

View File

@ -0,0 +1,286 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains a handy triangle class.
* \file IceTriangle.cpp
* \author Pierre Terdiman
* \date January, 17, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.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;
}
}

View File

@ -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__

View File

@ -0,0 +1,171 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains custom types.
* \file IceTypes.h
* \author Pierre Terdiman
* \date April, 4, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __ICETYPES_H__
#define __ICETYPES_H__
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Things to help us compile on non-windows platforms
#if defined(__MACOSX__) || defined(__APPLE__)
#undef bool
#define bool char
#undef true
#define true ((bool)-1)
#undef false
#define false ((bool)0)
#endif // mac stuff
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#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
typedef signed __int64 sqword; //!< sizeof(sqword) must be 8
typedef unsigned __int64 uqword; //!< sizeof(uqword) must be 8
typedef float float32; //!< sizeof(float32) must be 4
typedef double float64; //!< sizeof(float64) must be 4
ICE_COMPILE_TIME_ASSERT(sizeof(bool)==1); // ...otherwise things might fail with VC++ 4.2 !
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 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()
typedef int (__stdcall* PROC)(); //!< A standard procedure call.
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<class T> inline_ const T& TMin (const T& a, const T& b) { return b < a ? b : a; }
template<class T> inline_ const T& TMax (const T& a, const T& b) { return a < b ? b : a; }
template<class T> inline_ void TSetMin (T& a, const T& b) { if(a>b) a = b; }
template<class T> inline_ void TSetMax (T& a, const T& b) { if(a<b) a = b; }
#define SQR(x) ((x)*(x)) //!< Returns x square
#define CUBE(x) ((x)*(x)*(x)) //!< Returns x cube
#define AND & //!< ...
#define OR | //!< ...
#define XOR ^ //!< ...
#define QUADRAT(x) ((x)*(x)) //!< Returns x square
#ifdef _WIN32
# define srand48(x) srand((unsigned int) (x))
# define srandom(x) srand((unsigned int) (x))
# define random() ((double) rand())
# define drand48() ((double) (((double) rand()) / ((double) RAND_MAX)))
#endif
#endif // __ICETYPES_H__

View File

@ -0,0 +1,39 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains misc. useful macros & defines.
* \file IceUtils.cpp
* \author Pierre Terdiman (collected from various sources)
* \date April, 4, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.h"
using namespace IceCore;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 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)
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
udword IceCore::Alignment(udword address)
{
// Returns 0 for null addresses
if(!address) return 0;
// Test all bits
udword Align = 1;
for(udword i=1;i<32;i++)
{
// Returns as soon as the alignment is broken
if(address&Align) return Align;
Align<<=1;
}
// Here all bits are null, except the highest one (else the address would be null)
return Align;
}

View File

@ -0,0 +1,256 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains misc. useful macros & defines.
* \file IceUtils.h
* \author Pierre Terdiman (collected from various sources)
* \date April, 4, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __ICEUTILS_H__
#define __ICEUTILS_H__
#define START_RUNONCE { static bool __RunOnce__ = false; if(!__RunOnce__){
#define END_RUNONCE __RunOnce__ = true;}}
//! Reverse all the bits in a 32 bit word (from Steve Baker's Cute Code Collection)
//! (each line can be done in any order.
inline_ void ReverseBits(udword& n)
{
n = ((n >> 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<<n); }
//! Classic XOR swap (from Steve Baker's Cute Code Collection)
//! x ^= y; /* x' = (x^y) */
//! y ^= x; /* y' = (y^(x^y)) = x */
//! x ^= y; /* x' = (x^y)^x = y */
inline_ void Swap(udword& x, udword& y) { x ^= y; y ^= x; x ^= y; }
//! Little/Big endian (from Steve Baker's Cute Code Collection)
//!
//! Extra comments by Kenny Hoff:
//! Determines the byte-ordering of the current machine (little or big endian)
//! by setting an integer value to 1 (so least significant bit is now 1); take
//! the address of the int and cast to a byte pointer (treat integer as an
//! array of four bytes); check the value of the first byte (must be 0 or 1).
//! If the value is 1, then the first byte least significant byte and this
//! implies LITTLE endian. If the value is 0, the first byte is the most
//! significant byte, BIG endian. Examples:
//! integer 1 on BIG endian: 00000000 00000000 00000000 00000001
//! integer 1 on LITTLE endian: 00000001 00000000 00000000 00000000
//!---------------------------------------------------------------------------
//! int IsLittleEndian() { int x=1; return ( ((char*)(&x))[0] ); }
inline_ char LittleEndian() { int i = 1; return *((char*)&i); }
//!< Alternative abs function
inline_ udword abs_(sdword x) { sdword y= x >> 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<class Type> inline_ void TSwap(Type& a, Type& b) { const Type c = a; a = b; b = c; }
template<class Type> inline_ Type TClamp(const Type& x, const Type& lo, const Type& hi) { return ((x<lo) ? lo : (x>hi) ? hi : x); }
template<class Type> inline_ void TSort(Type& a, Type& b)
{
if(a>b) TSwap(a, b);
}
template<class Type> 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__

View File

@ -0,0 +1,696 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
* 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
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.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(udword(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;i<Nb;i++)
{
AABB_PRIM(i, OPC_CONTACT)
}
return true;
}
// Override destination array since we're only going to get leaf boxes here
mTouchedBoxes.Reset();
mTouchedPrimitives = &mTouchedBoxes;
// Now, do the actual query against leaf boxes
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 - 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;
}

View File

@ -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:
Container mTouchedBoxes;
};
#endif // __OPC_AABBCOLLIDER_H__

View File

@ -0,0 +1,573 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
* 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
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.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;i<mNbPrimitives;i++)
{
// Get index in global list
udword Index = mNodePrimitives[i];
// Test against the splitting value. The primitive value is tested against the enclosing-box center.
// [We only need an approximate partition of the enclosing box here.]
float PrimitiveValue = builder->GetSplittingValue(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;i<mNbPrimitives;i++)
{
udword Index = mNodePrimitives[i];
Means.x+=builder->GetSplittingValue(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;i<mNbPrimitives;i++)
{
udword Index = mNodePrimitives[i];
float Cx = builder->GetSplittingValue(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]<Results[Min]) Min = 1;
if(Results[2]<Results[Min]) Min = 2;
// Split along the axis
NbPos = Split(Min, builder);
// Check split validity
if(!NbPos || NbPos==mNbPrimitives) ValidSplit = false;
}
else if(builder->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]]<Keys[SortedAxis[i+1]])
{
udword Tmp = SortedAxis[i];
SortedAxis[i] = SortedAxis[i+1];
SortedAxis[i+1] = Tmp;
}
}
}
// Find the largest axis to split along
udword CurAxis = 0;
ValidSplit = false;
while(!ValidSplit && CurAxis!=3)
{
NbPos = Split(SortedAxis[CurAxis], builder);
// Check the subdivision has been successful
if(!NbPos || NbPos==mNbPrimitives) CurAxis++;
else ValidSplit = true;
}
}
else if(builder->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 = size_t(&Pool[Count+0])|1;
#ifndef OPC_NO_NEG_VANILLA_TREE
mNeg = size_t(&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 = (size_t)new AABBTreeNode; CHECKALLOC(mPos);
mNeg = (size_t)new AABBTreeNode; CHECKALLOC(mNeg);
#else
AABBTreeNode* PosNeg = new AABBTreeNode[2];
CHECKALLOC(PosNeg);
mPos = (size_t)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;i<builder->mNbPrimitives;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);
}

View File

@ -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 */ \
size_t 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 */ \
size_t mPos; /* "Positive" child */ \
size_t 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__

View File

@ -0,0 +1,138 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
* 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
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.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);
}

View File

@ -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__

View File

@ -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;
}

View File

@ -0,0 +1,367 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
* 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.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.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;i<nb0;i++) MinPosList0[i] = array0[i]->GetMin(Axis0);
for(udword i=0;i<nb1;i++) MinPosList1[i] = array1[i]->GetMin(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(RunningAddress1<LastSorted1 && Sorted0<LastSorted0)
{
Index0 = *Sorted0++;
while(RunningAddress1<LastSorted1 && MinPosList1[*RunningAddress1]<MinPosList0[Index0]) RunningAddress1++;
const udword* RunningAddress2_1 = RunningAddress1;
while(RunningAddress2_1<LastSorted1 && MinPosList1[Index1 = *RunningAddress2_1++]<=array0[Index0]->GetMax(Axis0))
{
if(array0[Index0]->Intersect(*array1[Index1], Axis1))
{
if(array0[Index0]->Intersect(*array1[Index1], Axis2))
{
pairs.AddPair(Index0, Index1);
}
}
}
}
////
while(RunningAddress0<LastSorted0 && Sorted1<LastSorted1)
{
Index0 = *Sorted1++;
while(RunningAddress0<LastSorted0 && MinPosList0[*RunningAddress0]<=MinPosList1[Index0]) RunningAddress0++;
const udword* RunningAddress2_0 = RunningAddress0;
while(RunningAddress2_0<LastSorted0 && MinPosList0[Index1 = *RunningAddress2_0++]<=array1[Index0]->GetMax(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;i<nb;i++) PosList[i] = array[i]->GetMin(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(RunningAddress<LastSorted && Sorted<LastSorted)
{
Index0 = *Sorted++;
// while(RunningAddress<LastSorted && PosList[*RunningAddress++]<PosList[Index0]);
while(PosList[*RunningAddress++]<PosList[Index0]);
if(RunningAddress<LastSorted)
{
const udword* RunningAddress2 = RunningAddress;
// while(RunningAddress2<LastSorted && PosList[Index1 = *RunningAddress2++]<=array[Index0]->GetMax(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;i<nb;i++) MinList[i] = array[i]->GetMin(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(RunningAddress<LastSorted && Sorted<LastSorted)
// while(RunningAddress<LastSorted)
while(RunningAddress<&Sorted[nb])
// while(Sorted<LastSorted)
{
// Index0 = *Sorted++;
Index0 = *RunningAddress++;
// while(RunningAddress<LastSorted && PosList[*RunningAddress++]<PosList[Index0]);
// while(PosList[*RunningAddress++]<PosList[Index0]);
//RunningAddress = Sorted;
// if(RunningAddress<LastSorted)
{
const udword* RunningAddress2 = RunningAddress;
// while(RunningAddress2<LastSorted && PosList[Index1 = *RunningAddress2++]<=array[Index0]->GetMax(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;i<nb0;i++)
{
for(udword j=0;j<nb1;j++)
{
if(array0[i]->Intersect(*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;i<nb;i++)
{
for(udword j=i+1;j<nb;j++)
{
if(array[i]->Intersect(*array[j])) pairs.AddPair(i, j);
}
}
return true;
}

View File

@ -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__

View File

@ -0,0 +1,54 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
* 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
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.h"
using namespace Opcode;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Constructor.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Collider::Collider() :
mFlags (0),
mCurrentModel (null),
mIMesh (null)
{
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Destructor.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Collider::~Collider()
{
}

View File

@ -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__

View File

@ -0,0 +1,48 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
* 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
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.h"
using namespace Opcode;

View File

@ -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)<GetMax(0)) return FALSE;
if(box.GetMax(1)<GetMax(1)) return FALSE;
if(box.GetMax(2)<GetMax(2)) return FALSE;
return TRUE;
}
Point mCenter; //!< Box center
Point mExtents; //!< Box extents
};
class OPCODE_API QuantizedAABB
{
public:
//! Constructor
inline_ QuantizedAABB() {}
//! Destructor
inline_ ~QuantizedAABB() {}
sword mCenter[3]; //!< Quantized center
uword mExtents[3]; //!< Quantized extents
};
//! Quickly rotates & translates a vector
inline_ void TransformPoint(Point& dest, const Point& source, const Matrix3x3& rot, const Point& trans)
{
dest.x = trans.x + source.x * rot.m[0][0] + source.y * rot.m[1][0] + source.z * rot.m[2][0];
dest.y = trans.y + source.x * rot.m[0][1] + source.y * rot.m[1][1] + source.z * rot.m[2][1];
dest.z = trans.z + source.x * rot.m[0][2] + source.y * rot.m[1][2] + source.z * rot.m[2][2];
}
#endif //__OPC_COMMON_H__

View File

@ -0,0 +1,466 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
* OPCODE - Optimized Collision Detection
* Copyright (C) 2001 Pierre Terdiman
* Homepage: http://www.codercorner.com/Opcode.htm
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains code for hybrid models.
* \file OPC_HybridModel.cpp
* \author Pierre Terdiman
* \date May, 18, 2003
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* An hybrid collision model.
*
* The problem :
*
* Opcode really shines for mesh-mesh collision, especially when meshes are deeply overlapping
* (it typically outperforms RAPID in those cases).
*
* Unfortunately this is not the typical scenario in games.
*
* For close-proximity cases, especially for volume-mesh queries, it's relatively easy to run faster
* than Opcode, that suffers from a relatively high setup time.
*
* In particular, Opcode's "vanilla" trees in those cases -can- run faster. They can also use -less-
* memory than the optimized ones, when you let the system stop at ~10 triangles / leaf for example
* (i.e. when you don't use "complete" trees). However, those trees tend to fragment memory quite a
* lot, increasing cache misses : since they're not "complete", we can't predict the final number of
* nodes and we have to allocate nodes on-the-fly. For the same reasons we can't use Opcode's "optimized"
* trees here, since they rely on a known layout to perform the "optimization".
*
* Hybrid trees :
*
* Hybrid trees try to combine best of both worlds :
*
* - they use a maximum limit of 16 triangles/leaf. "16" is used so that we'll be able to save the
* number of triangles using 4 bits only.
*
* - they're still "complete" trees thanks to a two-passes building phase. First we create a "vanilla"
* AABB-tree with Opcode, limited to 16 triangles/leaf. Then we create a *second* vanilla tree, this
* time using the leaves of the first one. The trick is : this second tree is now "complete"... so we
* can further transform it into an Opcode's optimized tree.
*
* - then we run the collision queries on that standard Opcode tree. The only difference is that leaf
* nodes contain indices to leaf nodes of another tree. Also, we have to skip all primitive tests in
* Opcode optimized trees, since our leaves don't contain triangles anymore.
*
* - finally, for each collided leaf, we simply loop through 16 triangles max, and collide them with
* the bounding volume used in the query (we only support volume-vs-mesh queries here, not mesh-vs-mesh)
*
* All of that is wrapped in this "hybrid model" that contains the minimal data required for this to work.
* It's a mix between old "vanilla" trees, and old "optimized" trees.
*
* Extra advantages:
*
* - If we use them for dynamic models, we're left with a very small number of leaf nodes to refit. It
* might be a bit faster since we have less nodes to write back.
*
* - In rigid body simulation, using temporal coherence and sleeping objects greatly reduce the actual
* influence of one tree over another (i.e. the speed difference is often invisible). So memory is really
* the key element to consider, and in this regard hybrid trees are just better.
*
* Information to take home:
* - they use less ram
* - they're not slower (they're faster or slower depending on cases, overall there's no significant
* difference *as long as objects don't interpenetrate too much* - in which case Opcode's optimized trees
* are still notably faster)
*
* \class HybridModel
* \author Pierre Terdiman
* \version 1.3
* \date May, 18, 2003
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.h"
using namespace Opcode;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Constructor.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
HybridModel::HybridModel() :
mNbLeaves (0),
mNbPrimitives (0),
mTriangles (null),
mIndices (null)
{
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Destructor.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
HybridModel::~HybridModel()
{
Release();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Releases everything.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void HybridModel::Release()
{
ReleaseBase();
DELETEARRAY(mIndices);
DELETEARRAY(mTriangles);
mNbLeaves = 0;
mNbPrimitives = 0;
}
struct Internal
{
Internal()
{
mNbLeaves = 0;
mLeaves = null;
mTriangles = null;
mBase = null;
}
~Internal()
{
DELETEARRAY(mLeaves);
}
udword mNbLeaves;
AABB* mLeaves;
LeafTriangles* mTriangles;
const udword* mBase;
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Builds a collision model.
* \param create [in] model creation structure
* \return true if success
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool HybridModel::Build(const OPCODECREATE& create)
{
// 1) Checkings
if(!create.mIMesh || !create.mIMesh->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((size_t(current->GetPrimitives()) - size_t(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;
}

View File

@ -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__

View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <float.h>
#include <math.h>
#ifndef ASSERT
#define ASSERT(exp) {}
#endif
#define ICE_COMPILE_TIME_ASSERT(exp) extern char ICE_Dummy[ (exp) ? 1 : -1 ]
#define Log {}
#define SetIceError(a,b) 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;

View File

@ -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(s2<mRadius2) return TRUE;
return FALSE;
}

View File

@ -0,0 +1,725 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
* 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.cpp
* \author Pierre Terdiman
* \date December, 28, 2002
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains a lss-vs-tree collider.
*
* \class LSSCollider
* \author Pierre Terdiman
* \version 1.3
* \date December, 28, 2002
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.h"
using namespace Opcode;
#include "OPC_LSSAABBOverlap.h"
#include "OPC_LSSTriOverlap.h"
#define SET_CONTACT(prim_index, flag) \
/* Set contact status */ \
mFlags |= flag; \
mTouchedPrimitives->Add(udword(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;i<Nb;i++)
{
LSS_PRIM(i, OPC_CONTACT)
}
return true;
}
// Override destination array since we're only going to get leaf boxes here
mTouchedBoxes.Reset();
mTouchedPrimitives = &mTouchedBoxes;
// Now, do the actual query against leaf boxes
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 - 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;
}

View File

@ -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:
Container mTouchedBoxes;
};
#endif // __OPC_LSSCOLLIDER_H__

View File

@ -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<fSqrDist) fSqrDist = fSqrDist0;
fSqrDist0 = OPC_PointTriangleSqrDist(rkSegOrigin, p0, p1, p2);
if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
}
else // region 3m
{
// min on face s=0 or r=0
kTriSeg.mOrig = p0;
kTriSeg.mDir = TriEdge1;
fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
fSqrDist0 = OPC_PointTriangleSqrDist(rkSegOrigin, p0, p1, p2);
if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
}
}
else if ( fT < 0.0f ) // region 5m
{
// min on face t=0 or r=0
kTriSeg.mOrig = p0;
kTriSeg.mDir = TriEdge0;
fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
fSqrDist0 = OPC_PointTriangleSqrDist(rkSegOrigin, p0, p1, p2);
if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
}
else // region 0m
{
// min on face r=0
fSqrDist = OPC_PointTriangleSqrDist(rkSegOrigin, p0, p1, p2);
}
}
else
{
if ( fS < 0.0f ) // region 2m
{
// min on face s=0 or s+t=1 or r=0
kTriSeg.mOrig = p0;
kTriSeg.mDir = TriEdge1;
fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
kTriSeg.mOrig = p1;
kTriSeg.mDir = TriEdge1-TriEdge0;
fSqrDist0 = OPC_SegmentRaySqrDist(segment, kTriSeg);
if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
fSqrDist0 = OPC_PointTriangleSqrDist(rkSegOrigin, p0, p1, p2);
if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
}
else if ( fT < 0.0f ) // region 6m
{
// min on face t=0 or s+t=1 or r=0
kTriSeg.mOrig = p0;
kTriSeg.mDir = TriEdge0;
fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
kTriSeg.mOrig = p1;
kTriSeg.mDir = TriEdge1-TriEdge0;
fSqrDist0 = OPC_SegmentRaySqrDist(segment, kTriSeg);
if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
fSqrDist0 = OPC_PointTriangleSqrDist(rkSegOrigin, p0, p1, p2);
if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
}
else // region 1m
{
// min on face s+t=1 or r=0
kTriSeg.mOrig = p1;
kTriSeg.mDir = TriEdge1-TriEdge0;
fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
fSqrDist0 = OPC_PointTriangleSqrDist(rkSegOrigin, p0, p1, p2);
if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
}
}
}
else if ( fR <= 1.0f )
{
if ( fS+fT <= 1.0f )
{
if ( fS < 0.0f )
{
if ( fT < 0.0f ) // region 4
{
// min on face s=0 or t=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<fSqrDist) fSqrDist = fSqrDist0;
}
else // region 3
{
// min on face s=0
kTriSeg.mOrig = p0;
kTriSeg.mDir = TriEdge1;
fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
}
}
else if ( fT < 0.0f ) // region 5
{
// min on face t=0
kTriSeg.mOrig = p0;
kTriSeg.mDir = TriEdge0;
fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
}
else // region 0
{
// global minimum is interior, done
fSqrDist = fR*(fA00*fR+fA01*fS+fA02*fT+2.0f*fB0)
+fS*(fA01*fR+fA11*fS+fA12*fT+2.0f*fB1)
+fT*(fA02*fR+fA12*fS+fA22*fT+2.0f*fB2)
+kDiff.SquareMagnitude();
}
}
else
{
if ( fS < 0.0f ) // region 2
{
// min on face s=0 or s+t=1
kTriSeg.mOrig = p0;
kTriSeg.mDir = TriEdge1;
fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
kTriSeg.mOrig = p1;
kTriSeg.mDir = TriEdge1-TriEdge0;
fSqrDist0 = OPC_SegmentRaySqrDist(segment, kTriSeg);
if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
}
else if ( fT < 0.0f ) // region 6
{
// min on face t=0 or s+t=1
kTriSeg.mOrig = p0;
kTriSeg.mDir = TriEdge0;
fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
kTriSeg.mOrig = p1;
kTriSeg.mDir = TriEdge1-TriEdge0;
fSqrDist0 = OPC_SegmentRaySqrDist(segment, kTriSeg);
if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
}
else // region 1
{
// min on face s+t=1
kTriSeg.mOrig = p1;
kTriSeg.mDir = TriEdge1-TriEdge0;
fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
}
}
}
else // fR > 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(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
kPt = rkSegOrigin+rkSegDirection;
fSqrDist0 = OPC_PointTriangleSqrDist(kPt, p0, p1, p2);
if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
}
else // region 3p
{
// min on face s=0 or r=1
kTriSeg.mOrig = p0;
kTriSeg.mDir = TriEdge1;
fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
kPt = rkSegOrigin+rkSegDirection;
fSqrDist0 = OPC_PointTriangleSqrDist(kPt, p0, p1, p2);
if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
}
}
else if ( fT < 0.0f ) // region 5p
{
// min on face t=0 or r=1
kTriSeg.mOrig = p0;
kTriSeg.mDir = TriEdge0;
fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
kPt = rkSegOrigin+rkSegDirection;
fSqrDist0 = OPC_PointTriangleSqrDist(kPt, p0, p1, p2);
if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
}
else // region 0p
{
// min face on r=1
kPt = rkSegOrigin+rkSegDirection;
fSqrDist = OPC_PointTriangleSqrDist(kPt, p0, p1, p2);
}
}
else
{
if ( fS < 0.0f ) // region 2p
{
// min on face s=0 or s+t=1 or r=1
kTriSeg.mOrig = p0;
kTriSeg.mDir = TriEdge1;
fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
kTriSeg.mOrig = p1;
kTriSeg.mDir = TriEdge1-TriEdge0;
fSqrDist0 = OPC_SegmentRaySqrDist(segment, kTriSeg);
if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
kPt = rkSegOrigin+rkSegDirection;
fSqrDist0 = OPC_PointTriangleSqrDist(kPt, p0, p1, p2);
if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
}
else if ( fT < 0.0f ) // region 6p
{
// min on face t=0 or s+t=1 or r=1
kTriSeg.mOrig = p0;
kTriSeg.mDir = TriEdge0;
fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
kTriSeg.mOrig = p1;
kTriSeg.mDir = TriEdge1-TriEdge0;
fSqrDist0 = OPC_SegmentRaySqrDist(segment, kTriSeg);
if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
kPt = rkSegOrigin+rkSegDirection;
fSqrDist0 = OPC_PointTriangleSqrDist(kPt, p0, p1, p2);
if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
}
else // region 1p
{
// min on face s+t=1 or r=1
kTriSeg.mOrig = p1;
kTriSeg.mDir = TriEdge1-TriEdge0;
fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
kPt = rkSegOrigin+rkSegDirection;
fSqrDist0 = OPC_PointTriangleSqrDist(kPt, p0, p1, p2);
if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
}
}
}
}
else
{
// segment and triangle are parallel
kTriSeg.mOrig = p0;
kTriSeg.mDir = TriEdge0;
fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
kTriSeg.mDir = TriEdge1;
fSqrDist0 = OPC_SegmentRaySqrDist(segment, kTriSeg);
if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
kTriSeg.mOrig = p1;
kTriSeg.mDir = TriEdge1 - TriEdge0;
fSqrDist0 = OPC_SegmentRaySqrDist(segment, kTriSeg);
if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
fSqrDist0 = OPC_PointTriangleSqrDist(rkSegOrigin, p0, p1, p2);
if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
kPt = rkSegOrigin+rkSegDirection;
fSqrDist0 = OPC_PointTriangleSqrDist(kPt, p0, p1, p2);
if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
}
return fabsf(fSqrDist);
}
inline_ BOOL LSSCollider::LSSTriOverlap(const Point& vert0, const Point& vert1, const Point& vert2)
{
// Stats
mNbVolumePrimTests++;
float s2 = OPC_SegmentTriangleSqrDist(mSeg, vert0, vert1, vert2);
if(s2<mRadius2) return TRUE;
return FALSE;
}

View File

@ -0,0 +1,303 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
* OPCODE - Optimized Collision Detection
* Copyright (C) 2001 Pierre Terdiman
* Homepage: http://www.codercorner.com/Opcode.htm
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains a mesh interface.
* \file OPC_MeshInterface.cpp
* \author Pierre Terdiman
* \date November, 27, 2002
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* This structure holds 3 vertex-pointers. It's mainly used by collision callbacks so that the app doesn't have
* to return 3 vertices to OPCODE (36 bytes) but only 3 pointers (12 bytes). It seems better but I never profiled
* the alternative.
*
* \class VertexPointers
* \author Pierre Terdiman
* \version 1.3
* \date March, 20, 2001
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* This class is an interface between us and user-defined meshes. Meshes can be defined in a lot of ways, and here we
* try to support most of them.
*
* Basically you have two options:
* - callbacks, if OPC_USE_CALLBACKS is defined in OPC_Settings.h.
* - else pointers.
*
* If using pointers, you can also use strides or not. Strides are used when OPC_USE_STRIDE is defined.
*
*
* CALLBACKS:
*
* Using callbacks is the most generic way to feed OPCODE with your meshes. Indeed, you just have to give
* access to three vertices at the end of the day. It's up to you to fetch them from your database, using
* whatever method you want. Hence your meshes can lie in system memory or AGP, be indexed or not, use 16
* or 32-bits indices, you can decompress them on-the-fly if needed, etc. On the other hand, a callback is
* called each time OPCODE needs access to a particular triangle, so there might be a slight overhead.
*
* To make things clear: geometry & topology are NOT stored in the collision system,
* in order to save some ram. So, when the system needs them to perform accurate intersection
* tests, you're requested to provide the triangle-vertices corresponding to a given face index.
*
* Ex:
*
* \code
* static void ColCallback(udword triangle_index, VertexPointers& triangle, udword user_data)
* {
* // Get back Mesh0 or Mesh1 (you also can use 2 different callbacks)
* Mesh* MyMesh = (Mesh*)user_data;
* // Get correct triangle in the app-controlled database
* const Triangle* Tri = MyMesh->GetTriangle(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
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.h"
using namespace Opcode;
Point MeshInterface::VertexCache[3];
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 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),
Single(true)
{
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 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<mNbTris;i++)
{
GetTriangle(VP, i);
if( (VP.Vertex[0]==VP.Vertex[1])
|| (VP.Vertex[1]==VP.Vertex[2])
|| (VP.Vertex[2]==VP.Vertex[0])) NbDegenerate++;
}
return NbDegenerate;
}
#ifdef OPC_USE_CALLBACKS
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 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 MeshInterface::SetCallback(RequestCallback callback, void* user_data)
{
if(!callback) return SetIceError("MeshInterface::SetCallback: callback pointer is null");
mObjCallback = callback;
mUserData = user_data;
return true;
}
#else
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 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 MeshInterface::SetPointers(const IndexedTriangle* tris, const Point* verts)
{
if(!tris || !verts) return SetIceError("MeshInterface::SetPointers: pointer is null", null);
mTris = tris;
mVerts = verts;
return true;
}
#ifdef OPC_USE_STRIDE
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 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 MeshInterface::SetStrides(udword tri_stride, udword vertex_stride)
{
if(tri_stride<sizeof(IndexedTriangle)) return SetIceError("MeshInterface::SetStrides: invalid triangle stride", null);
if(vertex_stride<sizeof(Point)) return SetIceError("MeshInterface::SetStrides: invalid vertex stride", null);
mTriStride = tri_stride;
mVertexStride = vertex_stride;
return true;
}
#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 MeshInterface::RemapClient(udword nb_indices, const udword* permutation) const
{
// Checkings
if(!nb_indices || !permutation) return false;
if(nb_indices!=mNbTris) return false;
#ifdef OPC_USE_CALLBACKS
// We can't really do that using callbacks
return false;
#else
IndexedTriangle* Tmp = new IndexedTriangle[mNbTris];
CHECKALLOC(Tmp);
#ifdef OPC_USE_STRIDE
udword Stride = mTriStride;
#else
udword Stride = sizeof(IndexedTriangle);
#endif
for(udword i=0;i<mNbTris;i++)
{
const IndexedTriangle* T = (const IndexedTriangle*)(((ubyte*)mTris) + i * Stride);
Tmp[i] = *T;
}
for(udword i=0;i<mNbTris;i++)
{
IndexedTriangle* T = (IndexedTriangle*)(((ubyte*)mTris) + i * Stride);
*T = Tmp[permutation[i]];
}
DELETEARRAY(Tmp);
#endif
return true;
}

View File

@ -0,0 +1,199 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
* OPCODE - Optimized Collision Detection
* Copyright (C) 2001 Pierre Terdiman
* Homepage: http://www.codercorner.com/Opcode.htm
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains a mesh interface.
* \file OPC_MeshInterface.h
* \author Pierre Terdiman
* \date November, 27, 2002
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __OPC_MESHINTERFACE_H__
#define __OPC_MESHINTERFACE_H__
struct VertexPointers
{
const Point* Vertex[3];
bool BackfaceCulling(const Point& source)
{
const Point& p0 = *Vertex[0];
const Point& p1 = *Vertex[1];
const Point& p2 = *Vertex[2];
// Compute normal direction
Point Normal = (p2 - p1)^(p0 - p1);
// Backface culling
return (Normal | (source - p0)) >= 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);
if (Single){
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);
}
else{
for (int i = 0; i < 3; i++){
const double* v = (const double*)(((ubyte*)mVerts) + T->mVRef[i] * mVertexStride);
VertexCache[i].x = (float)v[0];
VertexCache[i].y = (float)v[1];
VertexCache[i].z = (float)v[2];
vp.Vertex[i] = &VertexCache[i];
}
}
#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]];
#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
public:
bool Single; //!< Use single or double precision vertices
private:
static Point VertexCache[3];
#endif
};
#endif //__OPC_MESHINTERFACE_H__

View File

@ -0,0 +1,222 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
* 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
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.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();
}

View File

@ -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__

View File

@ -0,0 +1,767 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
* 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
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.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(udword(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 || f<mB1.x) return FALSE;\
f = p.x * mRModelToBox.m[0][1] + p.y * mRModelToBox.m[1][1] + p.z * mRModelToBox.m[2][1]; if(f>mB0.y || f<mB1.y) return FALSE;\
f = p.x * mRModelToBox.m[0][2] + p.y * mRModelToBox.m[1][2] + p.z * mRModelToBox.m[2][2]; if(f>mB0.z || f<mB1.z) return FALSE;
Point p;
float f;
TEST_PT(be.x, be.y, be.z)
TEST_PT(-be.x, be.y, be.z)
TEST_PT(be.x, -be.y, be.z)
TEST_PT(-be.x, -be.y, be.z)
TEST_PT(be.x, be.y, -be.z)
TEST_PT(-be.x, be.y, -be.z)
TEST_PT(be.x, -be.y, -be.z)
TEST_PT(-be.x, -be.y, -be.z)
return TRUE;
*/
// Yes there is:
// - compute model-box's AABB in OBB space
// - test AABB-in-AABB
float NCx = bc.x * mRModelToBox.m[0][0] + bc.y * mRModelToBox.m[1][0] + bc.z * mRModelToBox.m[2][0];
float NEx = fabsf(mRModelToBox.m[0][0] * be.x) + fabsf(mRModelToBox.m[1][0] * be.y) + fabsf(mRModelToBox.m[2][0] * be.z);
if(mB0.x < NCx+NEx) return FALSE;
if(mB1.x > 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;i<Nb;i++)
{
OBB_PRIM(i, OPC_CONTACT)
}
return true;
}
// Override destination array since we're only going to get leaf boxes here
mTouchedBoxes.Reset();
mTouchedPrimitives = &mTouchedBoxes;
// Now, do the actual query against leaf boxes
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 - 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;
}

View File

@ -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:
Container mTouchedBoxes;
};
#endif // __OPC_OBBCOLLIDER_H__

View File

@ -0,0 +1,782 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
* 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
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.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 = (size_t)&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 = (size_t)&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 = (size_t)&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;i<mNbNodes;i++) \
{ \
if(fabsf(Nodes[i].mAABB.mCenter.x)>CMax.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<<nbc)-1)/CMax.x : 0.0f; \
CQuantCoeff.y = CMax.y!=0.0f ? float((1<<nbc)-1)/CMax.y : 0.0f; \
CQuantCoeff.z = CMax.z!=0.0f ? float((1<<nbc)-1)/CMax.z : 0.0f; \
EQuantCoeff.x = EMax.x!=0.0f ? float((1<<nbe)-1)/EMax.x : 0.0f; \
EQuantCoeff.y = EMax.y!=0.0f ? float((1<<nbe)-1)/EMax.y : 0.0f; \
EQuantCoeff.z = EMax.z!=0.0f ? float((1<<nbe)-1)/EMax.z : 0.0f; \
/* Compute and save dequantization coeffs */ \
mCenterCoeff.x = CQuantCoeff.x!=0.0f ? 1.0f / CQuantCoeff.x : 0.0f; \
mCenterCoeff.y = CQuantCoeff.y!=0.0f ? 1.0f / CQuantCoeff.y : 0.0f; \
mCenterCoeff.z = CQuantCoeff.z!=0.0f ? 1.0f / CQuantCoeff.z : 0.0f; \
mExtentsCoeff.x = EQuantCoeff.x!=0.0f ? 1.0f / EQuantCoeff.x : 0.0f; \
mExtentsCoeff.y = EQuantCoeff.y!=0.0f ? 1.0f / EQuantCoeff.y : 0.0f; \
mExtentsCoeff.z = EQuantCoeff.z!=0.0f ? 1.0f / EQuantCoeff.z : 0.0f; \
#define PERFORM_QUANTIZATION \
/* Quantize */ \
mNodes[i].mAABB.mCenter[0] = sword(Nodes[i].mAABB.mCenter.x * CQuantCoeff.x); \
mNodes[i].mAABB.mCenter[1] = sword(Nodes[i].mAABB.mCenter.y * CQuantCoeff.y); \
mNodes[i].mAABB.mCenter[2] = sword(Nodes[i].mAABB.mCenter.z * CQuantCoeff.z); \
mNodes[i].mAABB.mExtents[0] = uword(Nodes[i].mAABB.mExtents.x * EQuantCoeff.x); \
mNodes[i].mAABB.mExtents[1] = uword(Nodes[i].mAABB.mExtents.y * EQuantCoeff.y); \
mNodes[i].mAABB.mExtents[2] = uword(Nodes[i].mAABB.mExtents.z * EQuantCoeff.z); \
/* Fix quantized boxes */ \
if(gFixQuantized) \
{ \
/* Make sure the quantized box is still valid */ \
Point Max = Nodes[i].mAABB.mCenter + Nodes[i].mAABB.mExtents; \
Point Min = Nodes[i].mAABB.mCenter - Nodes[i].mAABB.mExtents; \
/* For each axis */ \
for(udword j=0;j<3;j++) \
{ /* Dequantize the box center */ \
float qc = float(mNodes[i].mAABB.mCenter[j]) * mCenterCoeff[j]; \
bool FixMe=true; \
do \
{ /* Dequantize the box extent */ \
float qe = float(mNodes[i].mAABB.mExtents[j]) * mExtentsCoeff[j]; \
/* Compare real & dequantized values */ \
if(qc+qe<Max[j] || qc-qe>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 */ \
size_t Nb = (Data - size_t(Nodes))/Nodes[i].GetNodeSize(); \
Data = (size_t) &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
size_t Data;
for(udword i=0;i<mNbNodes;i++)
{
PERFORM_QUANTIZATION
REMAP_DATA(mData)
}
DELETEARRAY(Nodes);
}
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 AABBQuantizedTree::Refit(const MeshInterface* mesh_interface)
{
ASSERT(!"Not implemented since requantizing is painful !");
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 AABBQuantizedTree::Walk(GenericWalkingCallback callback, void* user_data) const
{
if(!callback) return false;
struct Local
{
static void _Walk(const AABBQuantizedNode* 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.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
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
size_t Data;
for(udword i=0;i<mNbNodes;i++)
{
PERFORM_QUANTIZATION
REMAP_DATA(mPosData)
REMAP_DATA(mNegData)
}
DELETEARRAY(Nodes);
}
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 AABBQuantizedNoLeafTree::Refit(const MeshInterface* mesh_interface)
{
ASSERT(!"Not implemented since requantizing is painful !");
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 AABBQuantizedNoLeafTree::Walk(GenericWalkingCallback callback, void* user_data) const
{
if(!callback) return false;
struct Local
{
static void _Walk(const AABBQuantizedNoLeafNode* 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;
}

View File

@ -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)!=0; } \
/* Data access */ \
inline_ const base_class* GetPos() const { return (base_class*)mData; } \
inline_ const base_class* GetNeg() const { return ((base_class*)mData)+1; } \
inline_ size_t GetPrimitive() const { return (mData>>1); } \
/* Stats */ \
inline_ udword GetNodeSize() const { return SIZEOFOBJECT; } \
\
volume mAABB; \
size_t 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)!=0; } \
inline_ BOOL HasNegLeaf() const { return (mNegData&1)!=0; } \
/* Data access */ \
inline_ const base_class* GetPos() const { return (base_class*)mPosData; } \
inline_ const base_class* GetNeg() const { return (base_class*)mNegData; } \
inline_ size_t GetPosPrimitive() const { return (mPosData>>1); } \
inline_ size_t GetNegPrimitive() const { return (mNegData>>1); } \
/* Stats */ \
inline_ udword GetNodeSize() const { return SIZEOFOBJECT; } \
\
volume mAABB; \
size_t mPosData; \
size_t 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__

View File

@ -0,0 +1,182 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
* 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
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.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.mDistance<CF->mDistance) *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;
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;
}
return false;
}
#endif

View File

@ -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__

Some files were not shown because too many files have changed in this diff Show More