Liquidfun links
@@ -0,0 +1,274 @@
|
||||
# Introduction
|
||||
|
||||
<img src="liquidfun-logo-square-small.png" alt="LiquidFun logo" style="float:right;"/>
|
||||
|
||||
[About](#about)<br/>
|
||||
[Prerequisites](#pre)<br/>
|
||||
[About this manual](#atm)<br/>
|
||||
[Feedback and reporting bugs](#frb)<br/>
|
||||
[Core concepts](#cc)<br/>
|
||||
[Modules](#mo)<br/>
|
||||
[Units](#un)<br/>
|
||||
[Factories and definitions](#fd)<br/>
|
||||
|
||||
|
||||
<a name="About"></a><br/>
|
||||
|
||||
## About
|
||||
|
||||
LiquidFun is based on Erin Catto's [Box2D library](http://www.box2d.org), which
|
||||
provides 2D, rigid-body simulation in games. LiquidFun extends Box2D to provide
|
||||
[particle physics and fluid dynamics](md__chapter11__particles.html).
|
||||
|
||||
Programmers can use LiquidFun in their games to make objects move in realistic
|
||||
ways and make the game world more interactive. From the game engine's point of
|
||||
view, a physics engine is just a system for procedural animation.
|
||||
|
||||
LiquidFun is written in portable C++. Most of the types defined in the engine
|
||||
begin with the b2 prefix. Hopefully this is sufficient to avoid name clashing
|
||||
with your game engine.
|
||||
|
||||
<a name="pre"></a><br/>
|
||||
## Prerequisites
|
||||
|
||||
In this manual I'll assume you are familiar with basic physics concepts, such
|
||||
as mass, force, torque, and impulses. If not, please first consult Google
|
||||
search and Wikipedia.
|
||||
|
||||
LiquidFun is based on the Box2D library, which was created as part of a
|
||||
physics tutorial at the Game Developer Conference. You can get these tutorials
|
||||
from the download section of Box2D.org.
|
||||
|
||||
Since LiquidFun is written in C++, you are expected to be experienced in C++
|
||||
programming. LiquidFun should not be your first C++ programming project! You
|
||||
should be comfortable with compiling, linking, and debugging.
|
||||
|
||||
Caution
|
||||
|
||||
LiquidFun should not be your first C++ project. Please learn C++
|
||||
programming, compiling, linking, and debugging before working with LiquidFun.
|
||||
There are many resources for this on the net.
|
||||
|
||||
<a name="atm"></a><br/>
|
||||
|
||||
## About this manual
|
||||
|
||||
This manual covers the majority of the LiquidFun API. However, not every
|
||||
aspect is covered. You are encouraged to look at the testbed included with
|
||||
LiquidFun to learn more. Also, the LiquidFun code base has comments formatted
|
||||
for Doxygen, so it is easy to create a hyper-linked API document.
|
||||
|
||||
This manual is only updated with new releases. The version in source control
|
||||
is likely to be out of date.
|
||||
|
||||
<a name="frb"></a><br/>
|
||||
## Feedback and Reporting Bugs
|
||||
|
||||
If you have a question or feedback about LiquidFun, please leave a comment in
|
||||
the forum. This is also a great place for community discussion.
|
||||
|
||||
LiquidFun issues are tracked using a Google code project. This is a great way
|
||||
to track issues and ensures that your issue will not be lost in the depths of
|
||||
the forums.
|
||||
|
||||
Please file bugs and feature requests here:
|
||||
[http://github.com/google/liquidfun/issues](http://github.com/google/liquidfun/issues)
|
||||
|
||||
You can help to ensure your issue gets fixed if you provide sufficient
|
||||
detail. A testbed example that reproduces the problem is ideal. You can read
|
||||
about the testbed later in this document.
|
||||
|
||||
<a name="cc"></a><br/>
|
||||
## Core Concepts
|
||||
|
||||
LiquidFun works with several fundamental concepts and objects. We briefly
|
||||
define these objects here and more details are given later in this
|
||||
document.<br/>
|
||||
<br/>
|
||||
### shape
|
||||
A shape is 2D geometrical object, such as a circle or polygon.<br/>
|
||||
|
||||
### rigid body
|
||||
A chunk of matter that is so strong that the distance between any two bits of
|
||||
matter on the chunk is constant. They are hard like a diamond. In the
|
||||
following discussion we use body interchangeably with rigid body.<br/>
|
||||
|
||||
### fixture
|
||||
A fixture binds a shape to a body and adds material properties such as
|
||||
density, friction, and restitution. A fixture puts a shape into the collision
|
||||
system (broad-phase) so that it can collide with other shapes.<br/>
|
||||
|
||||
### constraint
|
||||
A constraint is a physical connection that removes degrees of freedom from
|
||||
bodies. A 2D body has 3 degrees of freedom (two translation coordinates and
|
||||
one rotation coordinate). If we take a body and pin it to the wall (like a
|
||||
pendulum) we have constrained the body to the wall. At this point the body can
|
||||
only rotate about the pin, so the constraint has removed 2 degrees of
|
||||
freedom.<br/>
|
||||
|
||||
### contact constraint
|
||||
A special constraint designed to prevent penetration of rigid bodies and to
|
||||
simulate friction and restitution. You do not create contact constraints; they
|
||||
are created automatically by LiquidFun.<br/>
|
||||
|
||||
### joint
|
||||
This is a constraint used to hold two or more bodies together. LiquidFun
|
||||
supports several joint types: revolute, prismatic, distance, and more. Some
|
||||
joints may have limits and motors.<br/>
|
||||
|
||||
### joint limit
|
||||
A joint limit restricts the range of motion of a joint. For example, the human
|
||||
elbow only allows a certain range of angles.<br/>
|
||||
|
||||
### joint motor
|
||||
A joint motor drives the motion of the connected bodies according to the
|
||||
joint's degrees of freedom. For example, you can use a motor to drive the
|
||||
rotation of an elbow.<br/>
|
||||
|
||||
### world
|
||||
|
||||
A physics world is a collection of bodies, fixtures, and constraints that
|
||||
interact together. LiquidFun supports the creation of multiple worlds, but
|
||||
this is usually not necessary or desirable.<br/>
|
||||
|
||||
### solver
|
||||
The physics world has a solver that is used to advance time and to resolve
|
||||
contact and joint constraints. The LiquidFun solver is a high performance
|
||||
iterative solver that operates in order N time, where N is the number of
|
||||
constraints.<br/>
|
||||
|
||||
### continuous collision
|
||||
The solver advances bodies in time using discrete time steps. Without
|
||||
intervention this can lead to tunneling.<br/>
|
||||
<br/>
|
||||
|
||||
<img align="center" src="image_0.png" alt="Tunneling" height="293"
|
||||
width="275"><br/>
|
||||
|
||||
LiquidFun contains specialized algorithms to deal with tunneling. First, the
|
||||
collision algorithms can interpolate the motion of two bodies to find the
|
||||
first time of impact (TOI). Second, there is a sub-stepping solver that moves
|
||||
bodies to their first time of impact and then resolves the collision.
|
||||
|
||||
<a name="mo"></a><br/>
|
||||
## Modules
|
||||
|
||||
LiquidFun is composed of three modules: Common, Collision, and Dynamics. The
|
||||
Common module has code for allocation, math, and settings. The Collision
|
||||
module defines shapes, a broad-phase, and collision functions/queries. Finally
|
||||
the Dynamics module provides the simulation world, bodies, fixtures, and
|
||||
joints.<br/>
|
||||
<br/>
|
||||
|
||||
<img align="center" src="image_1.png" alt="Modules" height="229" width="217">
|
||||
<a name="un"></a><br/>
|
||||
## Units
|
||||
|
||||
LiquidFun works with floating point numbers and tolerances have to be used to
|
||||
make LiquidFun perform well. These tolerances have been tuned to work well
|
||||
with meters-kilogram-second (MKS) units. In particular, LiquidFun has been
|
||||
tuned to work well with moving shapes between 0.1 and 10 meters. So this means
|
||||
objects between soup cans and buses in size should work well. Static shapes
|
||||
may be up to 50 meters long without trouble.
|
||||
|
||||
Being a 2D physics engine, it is tempting to use pixels as your units.
|
||||
Unfortunately this will lead to a poor simulation and possibly weird behavior.
|
||||
An object of length 200 pixels would be seen by LiquidFun as the size of a 45
|
||||
story building.
|
||||
|
||||
Caution
|
||||
|
||||
LiquidFun is tuned for MKS units. Keep the size of moving objects
|
||||
roughly between 0.1 and 10 meters. You'll need to use some scaling system when
|
||||
you render your environment and actors. The LiquidFun testbed does this by
|
||||
using an OpenGL viewport transform. DO NOT USE PIXELS.
|
||||
|
||||
|
||||
|
||||
It is best to think of LiquidFun bodies as moving billboards upon which you
|
||||
attach your artwork. The billboard may move in a unit system of meters, but
|
||||
you can convert that to pixel coordinates with a simple scaling factor. You
|
||||
can then use those pixel coordinates to place your sprites, etc. You can also
|
||||
account for flipped coordinate axes.
|
||||
|
||||
LiquidFun uses radians for angles. The body rotation is stored in radians and
|
||||
may grow unbounded. Consider normalizing the angle of your bodies if the
|
||||
magnitude of the angle becomes too large (use b2Body::SetAngle).
|
||||
|
||||
Caution
|
||||
|
||||
LiquidFun uses radians, not degrees.
|
||||
|
||||
<a name="fd"></a><br/>
|
||||
## Factories and Definitions
|
||||
|
||||
Fast memory management plays a central role in the design of the LiquidFun
|
||||
API. So when you create a b2Body or a b2Joint, you need to call the factory
|
||||
functions on b2World. You should never try to allocate these types in another
|
||||
manner.
|
||||
|
||||
There are creation functions:
|
||||
|
||||
`b2Body* b2World::CreateBody(const b2BodyDef* def)`<br/>
|
||||
`b2Joint* b2World::CreateJoint(const b2JointDef* def)`<br/>
|
||||
`And there are corresponding destruction functions:`<br/>
|
||||
`void b2World::DestroyBody(b2Body* body)`<br/>
|
||||
`void b2World::DestroyJoint(b2Joint* joint)`<br/>
|
||||
|
||||
When you create a body or joint, you need to provide a definition. These
|
||||
definitions contain all the information needed to build the body or joint. By
|
||||
using this approach we can prevent construction errors, keep the number of
|
||||
function parameters small, provide sensible defaults, and reduce the number of
|
||||
accessors.
|
||||
|
||||
Since fixtures (shapes) must be parented to a body, they are created and
|
||||
destroyed using a factory method on b2Body:
|
||||
|
||||
`b2Fixture* b2Body::CreateFixture(const b2FixtureDef*
|
||||
def)`<br/>
|
||||
`void b2Body::DestroyFixture(b2Fixture* fixture)`<br/>
|
||||
|
||||
There is also shortcut to create a fixture directly from the shape and density.
|
||||
|
||||
`b2Fixture* b2Body::CreateFixture(const b2Shape* shape,
|
||||
float32 density)`<br/>
|
||||
|
||||
Factories do not retain references to the definitions. So you can create
|
||||
definitions on the stack and keep them in temporary resources.
|
||||
|
||||
*This content is licensed under
|
||||
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
|
||||
For details and restrictions, please see the
|
||||
[Content License](md__content_license.html).*
|
||||
|
||||
|
||||
[ ][Chapter02]
|
||||
[ ][Chapter03]
|
||||
[ ][Chapter04]
|
||||
[ ][Chapter05]
|
||||
[ ][Chapter06]
|
||||
[ ][Chapter07]
|
||||
[ ][Chapter08]
|
||||
[ ][Chapter09]
|
||||
[ ][Chapter10]
|
||||
[ ][Chapter11]
|
||||
[ ][Chapter12]
|
||||
[ ][Chapter13]
|
||||
[ ][Chapter14]
|
||||
[ ][Chapter15]
|
||||
|
||||
[Chapter02]: md__chapter02__hello__box2_d.html
|
||||
[Chapter03]: md__chapter03__common.html
|
||||
[Chapter04]: md__chapter04__collision__module.html
|
||||
[Chapter05]: md__chapter05__dynamics__module.html
|
||||
[Chapter06]: md__chapter06__bodies.html
|
||||
[Chapter07]: md__chapter07__fixtures.html
|
||||
[Chapter08]: md__chapter08__joints.html
|
||||
[Chapter09]: md__chapter09__contacts.html
|
||||
[Chapter10]: md__chapter10__world.html
|
||||
[Chapter11]: md__chapter11__particles.html
|
||||
[Chapter12]: md__chapter12__loose__ends.html
|
||||
[Chapter13]: md__chapter13__debug__drawing.html
|
||||
[Chapter14]: md__chapter14__limitations.html
|
||||
[Chapter15]: md__chapter15__references.html
|
||||
|
||||
@@ -0,0 +1,290 @@
|
||||
# Hello LiquidFun
|
||||
|
||||
[About](#about)<br/>
|
||||
[Creating a world](#cr)<br/>
|
||||
[Creating a ground box](#cgb)<br/>
|
||||
[Creating a dynamic body](#cdb)<br/>
|
||||
[Simulating the World (of LiquidFun)](#stw)<br>
|
||||
[Cleanup](#cl)<br>
|
||||
[The testbed](#tb)<br>
|
||||
<br/>
|
||||
|
||||
<a name="about">
|
||||
## About
|
||||
In the distribution of LiquidFun is a Hello World project. The program creates
|
||||
a large ground box and a small dynamic box. This code does not contain any
|
||||
graphics. All you will see is text output in the console of the box's position
|
||||
over time.
|
||||
|
||||
This is a good example of how to get up and running with LiquidFun.
|
||||
|
||||
<a name="cr"></a><br/>
|
||||
|
||||
## Creating a World
|
||||
|
||||
Every LiquidFun program begins with the creation of a b2World object. b2World
|
||||
is the physics hub that manages memory, objects, and simulation. You can
|
||||
allocate the physics world on the stack, heap, or data section.
|
||||
|
||||
It is easy to create a LiquidFun world. First, we define the gravity vector.
|
||||
|
||||
b2Vec2 gravity(0.0f, -10.0f);
|
||||
|
||||
Now we create the world object. Note that we are creating the world on the
|
||||
stack, so the world must remain in scope.
|
||||
|
||||
b2World world(gravity);
|
||||
|
||||
So now we have our physics world, let's start adding some stuff to it.
|
||||
|
||||
<a name="cgb"></a><br/>
|
||||
## Creating a Ground Box
|
||||
|
||||
Bodies are built using the following steps:
|
||||
|
||||
1. Define a body with position, damping, etc.
|
||||
|
||||
2. Use the world object to create the body.
|
||||
|
||||
3. Define fixtures with a shape, friction, density, etc.
|
||||
|
||||
4. Create fixtures on the body.
|
||||
|
||||
For step 1 we create the ground body. For this we need a body definition. With
|
||||
the body definition we specify the initial position of the ground body.
|
||||
|
||||
`b2BodyDef groundBodyDef;`<br/>
|
||||
`groundBodyDef.position.Set(0.0f, -10.0f);`<br/>
|
||||
|
||||
For step 2 the body definition is passed to the world object to create the
|
||||
ground body. The world object does not keep a reference to the body
|
||||
definition. Bodies are static by default. Static bodies don't collide with
|
||||
other static bodies and are immovable.
|
||||
|
||||
`b2Body* groundBody = world.CreateBody(&groundBodyDef);`<br/>
|
||||
|
||||
For step 3 we create a ground polygon. We use the SetAsBox shortcut to form
|
||||
the ground polygon into a box shape, with the box centered on the origin of
|
||||
the parent body.
|
||||
|
||||
`b2PolygonShape groundBox;`<br/>
|
||||
`groundBox.SetAsBox(50.0f, 10.0f);`<br/>
|
||||
|
||||
The SetAsBox function takes the **half**-**width** and **half**-**height**
|
||||
(extents). So in this case the ground box is 100 units wide (x-axis) and 20
|
||||
units tall (y-axis). LiquidFun is tuned for meters, kilograms, and seconds. So
|
||||
you can consider the extents to be in meters. LiquidFun generally works best
|
||||
when objects are the size of typical real world objects. For example, a barrel
|
||||
is about 1 meter tall. Due to the limitations of floating point arithmetic,
|
||||
using LiquidFun to model the movement of glaciers or dust particles is not a
|
||||
good idea.
|
||||
|
||||
We finish the ground body in step 4 by creating the shape fixture. For this
|
||||
step we have a shortcut. We do not have a need to alter the default fixture
|
||||
material properties, so we can pass the shape directly to the body without
|
||||
creating a fixture definition. Later we will see how to use a fixture
|
||||
definition for customized material properties. The second parameter is the
|
||||
shape density in kilograms per meter squared. A static body has zero mass by
|
||||
definition, so the density is not used in this case.
|
||||
|
||||
`groundBody->CreateFixture(&groundBox, 0.0f);`
|
||||
|
||||
LiquidFun does not keep a reference to the shape. It clones the data into a
|
||||
new b2Shape object.
|
||||
|
||||
Note that every fixture must have a parent body, even fixtures that are
|
||||
static. However, you can attach all static fixtures to a single static body.
|
||||
|
||||
When you attach a shape to a body using a fixture, the shape’s coordinates
|
||||
become local to the body. So when the body moves, so does the shape. A
|
||||
fixture’s world transform is inherited from the parent body. A fixture does
|
||||
not have a transform independent of the body. So we don’t move a shape
|
||||
around on the body. Moving or modifying a shape that is on a body is not
|
||||
supported. The reason is simple: a body with morphing shapes is not a rigid
|
||||
body, but LiquidFun is a rigid body engine. Many of the assumptions made in
|
||||
LiquidFun are based on the rigid body model. If this is violated many things
|
||||
will break
|
||||
|
||||
<a name="cdb"></a><br/>
|
||||
## Creating a Dynamic Body
|
||||
|
||||
So now we have a ground body. We can use the same technique to create a
|
||||
dynamic body. The main difference, besides dimensions, is that we must
|
||||
establish the dynamic body's mass properties.
|
||||
|
||||
First we create the body using CreateBody. By default bodies are static, so we
|
||||
should set the b2BodyType at construction time to make the body dynamic.
|
||||
|
||||
`b2BodyDef bodyDef;`<br/>
|
||||
`bodyDef.type = b2_dynamicBody;`<br/>
|
||||
`bodyDef.position.Set(0.0f, 4.0f);`<br/>
|
||||
`b2Body* body = world.CreateBody(&bodyDef);`<br/>
|
||||
|
||||
Caution
|
||||
|
||||
You must set the body type to b2_dynamicBody if you want the body to move in response to forces.
|
||||
|
||||
|
||||
|
||||
Next we create and attach a polygon shape using a fixture definition. First we
|
||||
create a box shape:
|
||||
|
||||
`b2PolygonShape dynamicBox;`<br/>
|
||||
`dynamicBox.SetAsBox(1.0f, 1.0f);`<br/>
|
||||
|
||||
Next we create a fixture definition using the box. Notice that we set density
|
||||
to 1. The default density is zero. Also, the friction on the shape is set to
|
||||
0.3.
|
||||
|
||||
`b2FixtureDef fixtureDef;`<br/>
|
||||
`fixtureDef.shape = &dynamicBox;`<br/>
|
||||
`fixtureDef.density = 1.0f;`<br/>
|
||||
`fixtureDef.friction = 0.3f;`<br/>
|
||||
|
||||
|
||||
Caution
|
||||
|
||||
A dynamic body should have at least one fixture with a non-zero density. Otherwise you will get strange behavior.
|
||||
|
||||
Using the fixture definition we can now create the fixture. This automatically
|
||||
updates the mass of the body. You can add as many fixtures as you like to a
|
||||
body. Each one contributes to the total mass.
|
||||
|
||||
`body->CreateFixture(&fixtureDef);`<br/>
|
||||
|
||||
That's it for initialization. We are now ready to begin simulating.
|
||||
|
||||
<a name="stw"></a><br/>
|
||||
## Simulating the World (of LiquidFun)
|
||||
|
||||
So we have initialized the ground box and a dynamic box. Now we are ready to
|
||||
set Newton loose to do his thing. We just have a couple more issues to
|
||||
consider.
|
||||
|
||||
LiquidFun uses a computational algorithm called an integrator. Integrators
|
||||
simulate the physics equations at discrete points of time. This goes along
|
||||
with the traditional game loop where we essentially have a flip book of
|
||||
movement on the screen. So we need to pick a time step for LiquidFun.
|
||||
Generally physics engines for games like a time step at least as fast as 60Hz
|
||||
or 1/60 seconds. You can get away with larger time steps, but you will have to
|
||||
be more careful about setting up the definitions for your world. We also don't
|
||||
like the time step to change much. A variable time step produces variable
|
||||
results, which makes it difficult to debug. So don't tie the time step to your
|
||||
frame rate (unless you really, really have to). Without further ado, here is
|
||||
the time step.
|
||||
|
||||
`float32 timeStep = 1.0f / 60.0f;`<br/>
|
||||
|
||||
In addition to the integrator, LiquidFun also uses a larger bit of code called
|
||||
a constraint solver. The constraint solver solves all the constraints in the
|
||||
simulation, one at a time. A single constraint can be solved perfectly.
|
||||
However, when we solve one constraint, we slightly disrupt other constraints.
|
||||
To get a good solution, we need to iterate over all constraints a number of
|
||||
times.
|
||||
|
||||
There are two phases in the constraint solver: a velocity phase and a position
|
||||
phase. In the velocity phase the solver computes the impulses necessary for
|
||||
the bodies to move correctly. In the position phase the solver adjusts the
|
||||
positions of the bodies to reduce overlap and joint detachment. Each phase has
|
||||
its own iteration count. In addition, the position phase may exit iterations
|
||||
early if the errors are small.
|
||||
|
||||
The suggested iteration count for LiquidFun is 8 for velocity and 3 for
|
||||
position. You can tune this number to your liking, just keep in mind that this
|
||||
has a trade-off between performance and accuracy. Using fewer iterations
|
||||
increases performance but accuracy suffers. Likewise, using more iterations
|
||||
decreases performance but improves the quality of your simulation. For this
|
||||
simple example, we don't need much iteration. Here are our chosen iteration
|
||||
counts.
|
||||
|
||||
`int32 velocityIterations = 6;`<br/>
|
||||
`int32 positionIterations = 2;`<br/>
|
||||
|
||||
Note that the time step and the iteration count are completely unrelated. An
|
||||
iteration is not a sub-step. One solver iteration is a single pass over all
|
||||
the constraints within a time step. You can have multiple passes over the
|
||||
constraints within a single time step.
|
||||
|
||||
We are now ready to begin the simulation loop. In your game the simulation
|
||||
loop can be merged with your game loop. In each pass through your game loop
|
||||
you call b2World::Step. Just one call is usually enough, depending on your
|
||||
frame rate and your physics time step.
|
||||
|
||||
The Hello World program was designed to be simple, so it has no graphical
|
||||
output. The code prints out the position and rotation of the dynamic body.
|
||||
Here is the simulation loop that simulates 60 time steps for a total of 1
|
||||
second of simulated time.
|
||||
|
||||
`for (int32 i = 0; i < 60; ++i)`<br/>
|
||||
`{`<br/>
|
||||
` world.Step(timeStep,
|
||||
velocityIterations, positionIterations);`<br/>
|
||||
` b2Vec2 position =
|
||||
body->GetPosition();`<br/>
|
||||
` float32 angle =
|
||||
body->GetAngle();`<br/>
|
||||
`printf("%4.2f %4.2f %4.2f\n", position.x,
|
||||
position.y, angle);`<br/>
|
||||
`}`
|
||||
|
||||
The output shows the box falling and landing on the ground box. Your output
|
||||
should look like this:
|
||||
|
||||
0.00 4.00 0.00
|
||||
|
||||
0.00 3.99 0.00
|
||||
|
||||
0.00 3.98 0.00
|
||||
|
||||
...
|
||||
|
||||
0.00 1.25 0.00
|
||||
|
||||
0.00 1.13 0.00
|
||||
|
||||
0.00 1.01 0.00
|
||||
|
||||
<a name="cl"></a><br/>
|
||||
## Cleanup
|
||||
|
||||
When a world leaves scope or is deleted by calling delete on a pointer, all
|
||||
the memory reserved for bodies, fixtures, and joints is freed. This is done to
|
||||
improve performance and make your life easier. However, you will need to
|
||||
nullify any body, fixture, or joint pointers you have because they will become
|
||||
invalid.
|
||||
|
||||
<a name="tb"></a><br/>
|
||||
## The Testbed
|
||||
|
||||
Once you have conquered the HelloWorld example, you should start looking at
|
||||
LiquidFun's testbed. The testbed is a unit-testing framework and demo
|
||||
environment. Here are some of the features:
|
||||
|
||||
* Camera with pan and zoom.
|
||||
|
||||
* Mouse picking of shapes attached to dynamic bodies.
|
||||
|
||||
* Extensible set of tests.
|
||||
|
||||
* GUI for selecting tests, parameter tuning, and debug drawing options.
|
||||
|
||||
* Pause and single step simulation.
|
||||
|
||||
* Text rendering
|
||||
|
||||
<img align="center" src="image_2.gif" alt="Modules" height="300"
|
||||
width="336"><br/>
|
||||
|
||||
The testbed has many examples of LiquidFun usage in the test cases and the
|
||||
framework itself. I encourage you to explore and tinker with the testbed as
|
||||
you learn LiquidFun.
|
||||
|
||||
Note: the testbed is written using freeglut and GLUI. The testbed is not part
|
||||
of the LiquidFun library. The LiquidFun library is agnostic about rendering.
|
||||
As shown by the HelloWorld example, you don't need a renderer to use LiquidFun.
|
||||
|
||||
|
||||
*This content is licensed under
|
||||
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
|
||||
For details and restrictions, please see the
|
||||
[Content License](md__content_license.html).*
|
||||
@@ -0,0 +1,93 @@
|
||||
# Common
|
||||
[About](#about)<br/>
|
||||
[Settings](#settings)<br/>
|
||||
[Memory Management](#mm)<br/>
|
||||
[Math](#math)
|
||||
|
||||
<a name="about">
|
||||
## About
|
||||
|
||||
The Common module contains settings, memory management, and vector math.
|
||||
|
||||
<a name="settings">
|
||||
## Settings
|
||||
|
||||
The header b2Settings.h contains:
|
||||
|
||||
* Types such as int32 and float32
|
||||
|
||||
* Constants
|
||||
|
||||
* Allocation wrappers
|
||||
|
||||
* The version number
|
||||
|
||||
### Types
|
||||
|
||||
LiquidFun defines various types such as float32, int8, etc. to make it easy to
|
||||
determine the size of structures.
|
||||
|
||||
### Constants
|
||||
|
||||
LiquidFun defines several constants. These are all documented in b2Settings.h.
|
||||
Normally you do not need to adjust these constants.
|
||||
|
||||
LiquidFun uses floating point math for collision and simulation. Due to
|
||||
round-off error some numerical tolerances are defined. Some tolerances are
|
||||
absolute and some are relative. Absolute tolerances use MKS units.
|
||||
|
||||
### Allocation wrappers
|
||||
|
||||
The settings file defines b2Alloc and b2Free for large allocations. You may
|
||||
forward these calls to your own memory management system.
|
||||
|
||||
### Version
|
||||
|
||||
The b2Version structure holds the current version so you can query this at
|
||||
run-time.
|
||||
|
||||
<a name="mm">
|
||||
## Memory Management
|
||||
|
||||
A large number of the decisions about the design of LiquidFun were based on
|
||||
the need for quick and efficient use of memory. In this section I will discuss
|
||||
how and why LiquidFun allocates memory.
|
||||
|
||||
LiquidFun tends to allocate a large number of small objects (around 50-300
|
||||
bytes). Using the system heap through malloc or new for small objects is
|
||||
inefficient and can cause fragmentation. Many of these small objects may have
|
||||
a short life span, such as contacts, but can persist for several time steps.
|
||||
So we need an allocator that can efficiently provide heap memory for these
|
||||
objects.
|
||||
|
||||
LiquidFun's solution is to use a small object allocator (SOA) called
|
||||
b2BlockAllocator. The SOA keeps a number of growable pools of varying sizes.
|
||||
When a request is made for memory, the SOA returns a block of memory that best
|
||||
fits the requested size. When a block is freed, it is returned to the pool.
|
||||
Both of these operations are fast and cause little heap traffic.
|
||||
|
||||
Since LiquidFun uses a SOA, you should never new or malloc a body, fixture, or
|
||||
joint. However, you do have to allocate a b2World on your own. The b2World
|
||||
class provides factories for you to create bodies, fixtures, and joints. This
|
||||
allows LiquidFun to use the SOA and hide the gory details from you. Never,
|
||||
call delete or free on a body, fixture, or joint.
|
||||
|
||||
While executing a time step, LiquidFun needs some temporary workspace memory.
|
||||
For this, it uses a stack allocator called b2StackAllocator to avoid per-step
|
||||
heap allocations. You don't need to interact with the stack allocator, but
|
||||
it's good to know it's there.
|
||||
|
||||
<a name="math">
|
||||
## Math
|
||||
|
||||
LiquidFun includes a simple small vector and matrix module. This has been
|
||||
designed to suit the internal needs of LiquidFun and the API. All the members
|
||||
are exposed, so you may use them freely in your application.
|
||||
|
||||
The math library is kept simple to make LiquidFun easy to port and maintain.
|
||||
|
||||
|
||||
*This content is licensed under
|
||||
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
|
||||
For details and restrictions, please see the
|
||||
[Content License](md__content_license.html).*
|
||||
@@ -0,0 +1,438 @@
|
||||
# Collision Module
|
||||
[About](#about)<br/>
|
||||
[Shapes](#shapes)<br/>
|
||||
[Unary Geometric Queries](#un)<br/>
|
||||
[Binary Functions](#bf)<br/>
|
||||
[Dynamic Tree](#dt)<br/>
|
||||
[Broad-Phase](#bp)<br/>
|
||||
|
||||
<a name="about">
|
||||
## About
|
||||
|
||||
The Collision module contains shapes and functions that operate on them. The
|
||||
module also contains a dynamic tree and broad-phase to acceleration collision
|
||||
processing of large systems.
|
||||
|
||||
The collision module is designed to be usable outside of the dynamic system.
|
||||
For example, you can use the dynamic tree for other aspects of your game
|
||||
besides physics.
|
||||
|
||||
However, the main purpose of LiquidFun is to provide a rigid body physics
|
||||
engine, so the using the collision module by itself may feel limited for some
|
||||
applications. Likewise, I will not make a strong effort to document it or
|
||||
polish the APIs.
|
||||
|
||||
<a name="shapes">
|
||||
## Shapes
|
||||
|
||||
Shapes describe collision geometry and may be used independently of physics
|
||||
simulation. At a minimum, you should understand how to create shapes that can
|
||||
be later attached to rigid bodies.
|
||||
|
||||
LiquidFun shapes implement the b2Shape base class. The base class defines
|
||||
functions to:
|
||||
|
||||
* Test a point for overlap with the shape.
|
||||
|
||||
* Perform a ray cast against the shape.
|
||||
|
||||
* Compute the shape's AABB.
|
||||
|
||||
* Compute the mass properties of the shape.
|
||||
|
||||
In addition, each shape has a type member and a radius. The radius even
|
||||
applies to polygons, as discussed below.
|
||||
|
||||
Keep in mind that a shape does not know about bodies and stand apart from the
|
||||
dynamics system. Shapes are stored in a compact form that is optimized for
|
||||
size and performance. As such, shapes are not easily moved around. You have to
|
||||
manually set the shape vertex positions to move a shape. However, when a shape
|
||||
is attached to a body using a fixture, the shapes move rigidly with the host
|
||||
body. In summary:
|
||||
|
||||
* When a shape is **not** attached to a body, you can view it’s vertices as
|
||||
being expressed in world-space.
|
||||
|
||||
* When a shape is attached to a body, you can view it’s vertices as being
|
||||
expressed in local coordinates.
|
||||
|
||||
### Circle Shapes
|
||||
|
||||
Circle shapes have a position and radius. Circles are solid. You cannot make
|
||||
a hollow circle using the circle shape.
|
||||
|
||||
`b2CircleShape circle;`<br/>
|
||||
`circle.m_p.Set(2.0f, 3.0f);`<br/>
|
||||
`circle.m_radius = 0.5f;`<br/>
|
||||
|
||||
### Polygon Shapes
|
||||
|
||||
Polygon shapes are solid convex polygons. A polygon is convex when all line
|
||||
segments connecting two points in the interior do not cross any edge of the
|
||||
polygon. Polygons are solid and never hollow. A polygon must have 3 or more
|
||||
vertices.
|
||||
|
||||
<img align="center" src="image_3.gif" alt="Polygon shapes" height="125"
|
||||
width="223"><br/>
|
||||
|
||||
|
||||
Polygon vertices are stored with a counter clockwise winding (CCW). We must be
|
||||
careful because the notion of CCW is with respect to a right-handed coordinate
|
||||
system with the z-axis pointing out of the plane. This might turn out to be
|
||||
clockwise on your screen, depending on your coordinate system conventions.
|
||||
|
||||
<img align="center" src="image_4.png" alt="Polygon vertices" height="176"
|
||||
width="347"><br/>
|
||||
|
||||
The polygon members are public, but you should use initialization functions to
|
||||
create a polygon. The initialization functions create normal vectors and
|
||||
perform validation.
|
||||
|
||||
You can create a polygon shape by passing in a vertex array. The maximal size
|
||||
of the array is controlled by b2_maxPolygonVertices which has a default value
|
||||
of 8. This is sufficient to describe most convex polygons.
|
||||
|
||||
The b2PolygonShape::Set function automatically computes the convex hull and
|
||||
establishes the proper winding order. This function is fast when the number of
|
||||
vertices is low. If you increase b2_maxPolygonVertices, then the convex hull
|
||||
computation might become slow. Also note that the convex hull function may
|
||||
eliminate and/or re-order the points you provide.
|
||||
|
||||
`// This defines a triangle in CCW order.`<br/>
|
||||
`b2Vec2 vertices[3];`<br/>
|
||||
`vertices[0].Set(0.0f, 0.0f);`<br/>
|
||||
`vertices[1].Set(1.0f, 0.0f);`<br/>
|
||||
`vertices[2].Set(0.0f, 1.0f);`<br/>
|
||||
`int32 count = 3;`<br/>
|
||||
`b2PolygonShape polygon;`<br/>
|
||||
`polygon.Set(vertices, count);`<br/>
|
||||
|
||||
The polygon shape has some convenience functions to create boxes.
|
||||
|
||||
`void SetAsBox(float32 hx, float32 hy);`<br/>
|
||||
`void SetAsBox(float32 hx, float32 hy, const b2Vec2& center,
|
||||
float32 angle);`<br/>
|
||||
|
||||
Polygons inherit a radius from b2Shape. The radius creates a skin around the
|
||||
polygon. The skin is used in stacking scenarios to keep polygons slightly
|
||||
separated. This allows continuous collision to work against the core polygon.
|
||||
|
||||
<img align="center" src="image_5.png" alt="Polygon skin" height="270"
|
||||
width="300"><br/>
|
||||
The polygon skin helps prevent tunneling by keeping the polygons separated.
|
||||
This results in small gaps between the shapes. Your visual representation can
|
||||
be larger than the polygon to hide any gaps.
|
||||
|
||||
<img align="center" src="image_6.png" alt="Polygons collide" height="109"
|
||||
width="322"><br/>
|
||||
|
||||
### Edge Shapes
|
||||
|
||||
Edge shapes are line segments. These are provided to assist in making a
|
||||
free-form static environment for your game. A major limitation of edge shapes
|
||||
is that they can collide with circles and polygons but not with themselves.
|
||||
The collision algorithms used by LiquidFun require that at least one of two
|
||||
colliding shapes have volume. Edge shapes have no volume, so edge-edge
|
||||
collision is not possible.
|
||||
|
||||
`// This an edge shape.`<br/>
|
||||
`b2Vec2 v1(0.0f, 0.0f);`<br/>
|
||||
`b2Vec2 v2(1.0f, 0.0f);`<br/>
|
||||
`b2EdgeShape edge;`<br/>
|
||||
`edge.Set(v1, v2);`<br/>
|
||||
|
||||
In many cases a game environment is constructed by connecting several edge
|
||||
shapes end-to-end. This can give rise to an unexpected artifact when a polygon
|
||||
slides along the chain of edges. In the figure below we see a box colliding
|
||||
with an internal vertex. These *ghost* collisions are caused when the polygon
|
||||
collides with an internal vertex generating an internal collision normal.
|
||||
|
||||
<img align="center" src="image_7.png" alt="Ghost collision" height="154"
|
||||
width="315"><br/>
|
||||
If edge1 did not exist this collision would seem fine. With edge1 present, the
|
||||
internal collision seems like a bug. But normally when LiquidFun collides two
|
||||
shapes, it views them in isolation.
|
||||
|
||||
Fortunately, the edge shape provides a mechanism for eliminating ghost
|
||||
collisions by storing the adjacent *ghost *vertices. LiquidFun uses these
|
||||
ghost vertices to prevent internal collisions.
|
||||
|
||||
<img align="center" src="image_8.png" alt="Ghost vertices" height="153"
|
||||
width="332"><br/>
|
||||
`// This is an edge shape with ghost vertices.`<br/>
|
||||
`b2Vec2 v0(1.7f, 0.0f);`<br/>
|
||||
`b2Vec2 v1(1.0f, 0.25f);`<br/>
|
||||
`b2Vec2 v2(0.0f, 0.0f);`<br/>
|
||||
`b2Vec2 v3(-1.7f, 0.4f);`<br/>
|
||||
`b2EdgeShape edge;`<br/>
|
||||
`edge.Set(v1, v2);`<br/>
|
||||
`edge.m_hasVertex0 = true;`<br/>
|
||||
`edge.m_hasVertex3 = true;`<br/>
|
||||
`edge.m_vertex0 = v0;`<br/>
|
||||
`edge.m_vertex3 = v3;`<br/>
|
||||
|
||||
In general stitching edges together this way is a bit wasteful and tedious.
|
||||
This brings us to chain shapes.
|
||||
|
||||
### Chain Shapes
|
||||
|
||||
The chain shape provides an efficient way to connect many edges together to
|
||||
construct your static game worlds. Chain shapes automatically eliminate ghost
|
||||
collisions and provide two-sided collision.
|
||||
|
||||
<img align="center" src="image_9.png" alt="Chain shape" height="165"
|
||||
width="305"><br/>
|
||||
`// This a chain shape with isolated vertices`<br/>
|
||||
`b2Vec2 vs[4];`<br/>
|
||||
`vs[0].Set(1.7f, 0.0f);`<br/>
|
||||
`vs[1].Set(1.0f, 0.25f);`<br/>
|
||||
`vs[2].Set(0.0f, 0.0f);`<br/>
|
||||
`vs[3].Set(-1.7f, 0.4f);`<br/>
|
||||
`b2ChainShape chain;`<br/>
|
||||
`chain.CreateChain(vs, 4);`<br/>
|
||||
|
||||
You may have a scrolling game world and would like to connect several chains
|
||||
together. You can connect chains together using ghost vertices, like we did
|
||||
with b2EdgeShape.
|
||||
|
||||
`// Install ghost vertices`<br/>
|
||||
`chain.SetPrevVertex(b2Vec2(3.0f, 1.0f));`<br/>
|
||||
`chain.SetNextVertex(b2Vec2(-2.0f, 0.0f));`<br/>
|
||||
|
||||
You may also create loops automatically.
|
||||
|
||||
`// Create a loop. The first and last vertices are
|
||||
connected.`<br/>
|
||||
`b2ChainShape chain;`<br/>
|
||||
`chain.CreateLoop(vs, 4);`<br/>
|
||||
|
||||
Self-intersection of chain shapes is not supported. It might work, it might
|
||||
not. The code that prevents ghost collisions assumes there are no
|
||||
self-intersections of the chain. Also, very close vertices can cause problems.
|
||||
Make sure all your edges are longer than b2_linearSlop (5mm).
|
||||
|
||||
<img align="center" src="image_10.png" alt="No self-intersection" height="178"
|
||||
width="335"><br/>
|
||||
Each edge in the chain is treated as a child shape and can be accessed by
|
||||
index. When a chain shape is connected to a body, each edge gets its own
|
||||
bounding box in the broad-phase collision tree.
|
||||
|
||||
`// Visit each child edge.`<br/>
|
||||
`for (int32 i = 0; i < chain.GetChildCount(); ++i)`<br/>
|
||||
`{`<br/>
|
||||
`b2EdgeShape edge;`<br/>
|
||||
`chain.GetChildEdge(&edge, i);`<br/>
|
||||
`…`<br/>
|
||||
`}`<br/>
|
||||
|
||||
<a name="un">
|
||||
## Unary Geometric Queries
|
||||
|
||||
You can perform a couple of geometric queries on a single shape.
|
||||
|
||||
### Shape Point Test
|
||||
|
||||
You can test a point for overlap with a shape. You provide a transform for the
|
||||
shape and a world point.
|
||||
|
||||
`b2Transfrom transform;`<br/>
|
||||
`transform.SetIdentity();`<br/>
|
||||
`b2Vec2 point(5.0f, 2.0f);`<br/>
|
||||
`bool hit = shape->TestPoint(transform, point);`<br/>
|
||||
|
||||
Edge and chain shapes always return false, even if the chain is a loop.
|
||||
|
||||
### Shape Ray Cast
|
||||
|
||||
You can cast a ray at a shape to get the point of first intersection and
|
||||
normal vector. No hit will register if the ray starts inside the shape. A
|
||||
child index is included for chain shapes because the ray cast will only check
|
||||
a single edge at a time.
|
||||
|
||||
`b2Transfrom transform;`<br/>
|
||||
`transform.SetIdentity();`<br/>
|
||||
`b2RayCastInput input;`<br/>
|
||||
`input.p1.Set(0.0f, 0.0f, 0.0f);`<br/>
|
||||
`input.p2.Set(1.0f, 0.0f, 0.0f);`<br/>
|
||||
`input.maxFraction = 1.0f;`<br/>
|
||||
`int32 childIndex = 0;`<br/>
|
||||
`b2RayCastOutput output;`<br/>
|
||||
`bool hit = shape->RayCast(&output, input, transform,
|
||||
childIndex);`<br/>
|
||||
`if (hit)`<br/>
|
||||
`{`<br/>
|
||||
`b2Vec2 hitPoint = input.p1 +
|
||||
output.fraction * (input.p2 – input.p1);`<br/>
|
||||
`…`<br/>
|
||||
`}`<br/>
|
||||
|
||||
<a name="bf">
|
||||
## Binary Functions
|
||||
|
||||
The Collision module contains bilateral functions that take a pair of shapes
|
||||
and compute some results. These include:
|
||||
|
||||
* Overlap
|
||||
|
||||
* Contact manifolds
|
||||
|
||||
* Distance
|
||||
|
||||
* Time of impact
|
||||
|
||||
### Overlap
|
||||
|
||||
You can test two shapes for overlap using this function:
|
||||
|
||||
`b2Transform xfA = …, xfB = …;`<br/>
|
||||
`bool overlap = b2TestOverlap(shapeA, indexA, shapeB,
|
||||
indexB, xfA, xfB);`<br/>
|
||||
|
||||
Again you must provide child indices to for the case of chain shapes.
|
||||
|
||||
### Contact Manifolds
|
||||
|
||||
LiquidFun has functions to compute contact points for overlapping shapes. If
|
||||
we consider circle-circle or circle-polygon, we can only get one contact point
|
||||
and normal. In the case of polygon-polygon we can get two points. These points
|
||||
share the same normal vector so LiquidFun groups them into a manifold
|
||||
structure. The contact solver takes advantage of this to improve stacking
|
||||
stability.
|
||||
|
||||
<img align="center" src="image_11.png" alt="Manifold structure" height="300"
|
||||
width="419"><br/>
|
||||
Normally you don’t need to compute contact manifolds directly, however you
|
||||
will likely use the results produced in the simulation.
|
||||
|
||||
The b2Manifold structure holds a normal vector and up to two contact points.
|
||||
The normal and points are held in local coordinates. As a convenience for the
|
||||
contact solver, each point stores the normal and tangential (friction)
|
||||
impulses.
|
||||
|
||||
The data stored in b2Manifold is optimized for internal use. If you need this
|
||||
data, it is usually best to use the b2WorldManifold structure to generate the
|
||||
world coordinates of the contact normal and points. You need to provide a
|
||||
b2Manifold and the shape transforms and radii.
|
||||
|
||||
`b2WorldManifold worldManifold;`<br/>
|
||||
`worldManifold.Initialize(&manifold, transformA,
|
||||
shapeA.m_radius, transformB, shapeB.m_radius);`<br/>
|
||||
`for (int32 i = 0; i < manifold.pointCount; ++i)`<br/>
|
||||
`{`<br/>
|
||||
`b2Vec2 point =
|
||||
worldManifold.points[i];`<br/>
|
||||
`…`<br/>
|
||||
`}`<br/>
|
||||
|
||||
Notice that the world manifold uses the point count from the original manifold.
|
||||
|
||||
During simulation shapes may move and the manifolds may change. Points may be
|
||||
added or removed. You can detect this using b2GetPointStates.
|
||||
|
||||
`b2PointState state1[2], state2[2];`<br/>
|
||||
`b2GetPointStates(state1, state2, &manifold1,
|
||||
&manifold2);`<br/>
|
||||
`if (state1[0] == b2_removeState)`<br/>
|
||||
`{`<br/>
|
||||
`// process event`<br/>
|
||||
`}`<br/>
|
||||
|
||||
### Distance
|
||||
|
||||
The b2Distance function can be used to compute the distance between two
|
||||
shapes. The distance function needs both shapes to be converted into a
|
||||
b2DistanceProxy. There is also some caching used to warm start the distance
|
||||
function for repeated calls. You can see the details in b2Distance.h.
|
||||
|
||||
<img align="center" src="image_12.png" alt="Distance" height="134"
|
||||
width="271"><br/>
|
||||
### Time of Impact
|
||||
|
||||
If two shapes are moving fast, they may *tunnel* through each other in a
|
||||
single time step.
|
||||
|
||||
<img align="center" src="image_13.png" alt="Time-of-impact tunneling"
|
||||
height="133" width="234"><br/>
|
||||
The b2TimeOfImpact function is used to determine the time when two moving
|
||||
shapes collide. This is called the *time of impact *(TOI). The main purpose of
|
||||
b2TimeOfImpact is for tunnel prevention. In particular, it is designed to
|
||||
prevent moving objects from tunneling outside of static level geometry.
|
||||
|
||||
This function accounts for rotation and translation of both shapes, however if
|
||||
the rotations are large enough, then the function may miss a collision.
|
||||
However the function will still report a non-overlapped time and will capture
|
||||
all translational collisions.
|
||||
|
||||
The time of impact function identities an initial separating axis and ensures
|
||||
the shapes do not cross on that axis. This might miss collisions that are
|
||||
clear at the final positions. While this approach may miss some collisions, it
|
||||
is very fast and adequate for tunnel prevention.
|
||||
|
||||
<img align="center" src="image_14.png" alt="Captured collision" height="169"
|
||||
width="297"><br/>
|
||||
<img align="center" src="image_15.png" alt="Missed collision" height="172"
|
||||
width="297"><br/>
|
||||
It is difficult to put a restriction on the rotation magnitude. There may be
|
||||
cases where collisions are missed for small rotations. Normally, these missed
|
||||
rotational collisions should not harm game play. They tend to be glancing
|
||||
collisions.
|
||||
|
||||
The function requires two shapes (converted to b2DistanceProxy) and two
|
||||
b2Sweep structures. The sweep structure defines the initial and final
|
||||
transforms of the shapes.
|
||||
|
||||
You can use fixed rotations to perform a *shape cast*. In this case, the time
|
||||
of impact function will not miss any collisions.
|
||||
|
||||
<a name="dt">
|
||||
## Dynamic Tree
|
||||
|
||||
The b2DynamicTree class is used by LiquidFun to organize large numbers of
|
||||
shapes efficiently. The class does not know about shapes. Instead it operates
|
||||
on axis-aligned bounding boxes (AABBs) with user data pointers.
|
||||
|
||||
The dynamic tree is a hierarchical AABB tree. Each internal node in the tree
|
||||
has two children. A leaf node is a single user AABB. The tree uses rotations
|
||||
to keep the tree balanced, even in the case of degenerate input.
|
||||
|
||||
The tree structure allows for efficient ray casts and region queries. For
|
||||
example, you may have hundreds of shapes in your scene. You could perform a
|
||||
ray cast against the scene in a brute force manner by ray casting each shape.
|
||||
This would be inefficient because it does not take advantage of shapes being
|
||||
spread out. Instead, you can maintain a dynamic tree and perform ray casts
|
||||
against the tree. This traverses the ray through the tree skipping large
|
||||
numbers of shapes.
|
||||
|
||||
A region query uses the tree to find all leaf AABBs that overlap a query AABB.
|
||||
This is faster than a brute force approach because many shapes can be skipped.
|
||||
|
||||
<img align="center" src="image_16.png" alt="Ray cast" height="249"
|
||||
width="248"><br/>
|
||||
<img align="center" src="image_17.png" alt="Region query" height="210"
|
||||
width="231"><br/>
|
||||
Normally you will not use the dynamic tree directly. Rather you will go
|
||||
through the b2World class for ray casts and region queries. If you plan to
|
||||
instantiate your own dynamic tree, you can learn how to use it by looking at
|
||||
how LiquidFun uses it.
|
||||
|
||||
<a name="bp">
|
||||
## Broad-phase
|
||||
|
||||
Collision processing in a physics step can be divided into narrow-phase and
|
||||
broad-phase. In the narrow-phase we compute contact points between pairs of
|
||||
shapes. Imagine we have N shapes. Using brute force, we would need to perform
|
||||
the narrow-phase for N*N/2 pairs.
|
||||
|
||||
The b2BroadPhase class reduces this load by using a dynamic tree for pair
|
||||
management. This greatly reduces the number of narrow-phase calls.
|
||||
|
||||
Normally you do not interact with the broad-phase directly. Instead, LiquidFun
|
||||
creates and manages a broad-phase internally. Also, b2BroadPhase is designed
|
||||
with LiquidFun’s simulation loop in mind, so it is likely not suited for
|
||||
other use cases.
|
||||
|
||||
|
||||
*This content is licensed under
|
||||
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
|
||||
For details and restrictions, please see the
|
||||
[Content License](md__content_license.html).*
|
||||
@@ -0,0 +1,37 @@
|
||||
# Dynamics Module
|
||||
|
||||
[Overview](#ov)<br/>
|
||||
|
||||
<a name="ov">
|
||||
## Overview
|
||||
|
||||
The Dynamics module is the most complex part of LiquidFun and is the part you
|
||||
likely interact with the most. The Dynamics module sits on top of the Common
|
||||
and Collision modules, so you should be somewhat familiar with those by now.
|
||||
|
||||
The Dynamics module contains:
|
||||
|
||||
* fixture class
|
||||
|
||||
* rigid body class
|
||||
|
||||
* contact class
|
||||
|
||||
* joint classes
|
||||
|
||||
* world class
|
||||
|
||||
* listener classes
|
||||
|
||||
There are many dependencies between these classes so it is difficult to
|
||||
describe one class without referring to another. In the following, you may see
|
||||
some references to classes that have not been described yet. Therefore, you
|
||||
may want to quickly skim this chapter before reading it closely.
|
||||
|
||||
The dynamics module is covered in the following chapters.
|
||||
|
||||
|
||||
*This content is licensed under
|
||||
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
|
||||
For details and restrictions, please see the
|
||||
[Content License](md__content_license.html).*
|
||||
@@ -0,0 +1,363 @@
|
||||
# Bodies
|
||||
|
||||
[About](#about)<br/>
|
||||
[Body Definition](#bd)<br/>
|
||||
[Body Factory](#bf)<br/>
|
||||
[Using a Body](#ub)<br/>
|
||||
|
||||
|
||||
<a name="about">
|
||||
## About
|
||||
|
||||
Bodies have position and velocity. You can apply forces, torques, and impulses
|
||||
to bodies. Bodies can be static, kinematic, or dynamic. Here are the body type
|
||||
definitions:
|
||||
|
||||
### b2_staticBody
|
||||
|
||||
A static body does not move under simulation and behaves as if it has infinite
|
||||
mass. Internally, LiquidFun stores zero for the mass and the inverse mass.
|
||||
Static bodies can be moved manually by the user. A static body has zero
|
||||
velocity. Static bodies do not collide with other static or kinematic bodies.
|
||||
|
||||
### b2_kinematicBody
|
||||
|
||||
A kinematic body moves under simulation according to its velocity. Kinematic
|
||||
bodies do not respond to forces. They can be moved manually by the user, but
|
||||
normally a kinematic body is moved by setting its velocity. A kinematic body
|
||||
behaves as if it has infinite mass, however, LiquidFun stores zero for the
|
||||
mass and the inverse mass. Kinematic bodies do not collide with other
|
||||
kinematic or static bodies.
|
||||
|
||||
### b2_dynamicBody
|
||||
|
||||
A dynamic body is fully simulated. They can be moved manually by the user, but
|
||||
normally they move according to forces. A dynamic body can collide with all
|
||||
body types. A dynamic body always has finite, non-zero mass. If you try to set
|
||||
the mass of a dynamic body to zero, it will automatically acquire a mass of
|
||||
one kilogram and it won’t rotate.
|
||||
|
||||
Bodies are the backbone for fixtures (shapes). Bodies carry fixtures and move
|
||||
them around in the world. Bodies are always rigid bodies in LiquidFun. That
|
||||
means that two fixtures attached to the same rigid body never move relative to
|
||||
each other and fixtures attached to the same body don’t collide.
|
||||
|
||||
Fixtures have collision geometry and density. Normally, bodies acquire their
|
||||
mass properties from the fixtures. However, you can override the mass
|
||||
properties after a body is constructed.
|
||||
|
||||
You usually keep pointers to all the bodies you create. This way you can query
|
||||
the body positions to update the positions of your graphical entities. You
|
||||
should also keep body pointers so you can destroy them when you are done with
|
||||
them.
|
||||
|
||||
<a name="bd">
|
||||
## Body Definition
|
||||
|
||||
Before a body is created you must create a body definition (b2BodyDef). The
|
||||
body definition holds the data needed to create and initialize a body.
|
||||
|
||||
LiquidFun copies the data out of the body definition; it does not keep a
|
||||
pointer to the body definition. This means you can recycle a body definition
|
||||
to create multiple bodies.
|
||||
|
||||
Let’s go over some of the key members of the body definition.
|
||||
|
||||
### Body Type
|
||||
|
||||
As discussed at the beginning of this chapter, there are three different body
|
||||
types: static, kinematic, and dynamic. You should establish the body type at
|
||||
creation because changing the body type later is expensive.
|
||||
|
||||
`bodyDef.type = b2_dynamicBody;`<br/>
|
||||
|
||||
Setting the body type is mandatory.
|
||||
|
||||
### Position and Angle
|
||||
|
||||
The body definition gives you the chance to initialize the position of the
|
||||
body on creation. This has far better performance than creating the body at
|
||||
the world origin and then moving the body.
|
||||
|
||||
Caution
|
||||
|
||||
Do not create a body at the origin and then move it. If you create
|
||||
several bodies at the origin, then performance will suffer.
|
||||
|
||||
|
||||
|
||||
A body has two main points of interest. The first point is the body's origin.
|
||||
Fixtures and joints are attached relative to the body's origin. The second
|
||||
point of interest is the center of mass. The center of mass is determined from
|
||||
mass distribution of the attached shapes or is explicitly set with b2MassData.
|
||||
Much of LiquidFun's internal computations use the center of mass position. For
|
||||
example b2Body stores the linear velocity for the center of mass.
|
||||
|
||||
When you are building the body definition, you may not know where the center
|
||||
of mass is located. Therefore you specify the position of the body's origin.
|
||||
You may also specify the body's angle in radians, which is not affected by the
|
||||
position of the center of mass. If you later change the mass properties of the
|
||||
body, then the center of mass may move on the body, but the origin position
|
||||
does not change and the attached shapes and joints do not move.
|
||||
|
||||
`bodyDef.position.Set(0.0f, 2.0f); // the body's origin
|
||||
position.`<br/>
|
||||
`bodyDef.angle = 0.25f * b2_pi; // the body's angle in
|
||||
radians.`<br/>
|
||||
|
||||
A rigid body is also a frame of reference. You can define fixtures and joints
|
||||
in that frame. Those fixtures and joint anchors never move in the local frame
|
||||
of the body.
|
||||
|
||||
### Damping
|
||||
|
||||
Damping is used to reduce the world velocity of bodies. Damping is different
|
||||
than friction because friction only occurs with contact. Damping is not a
|
||||
replacement for friction and the two effects should be used together.
|
||||
|
||||
Damping parameters should be between 0 and infinity, with 0 meaning no
|
||||
damping, and infinity meaning full damping. Normally you will use a damping
|
||||
value between 0 and 0.1. I generally do not use linear damping because it
|
||||
makes bodies look floaty.
|
||||
|
||||
`bodyDef.linearDamping = 0.0f;`<br/>
|
||||
`bodyDef.angularDamping = 0.01f;`<br/>
|
||||
|
||||
Damping is approximated for stability and performance. At small damping values
|
||||
the damping effect is mostly independent of the time step. At larger damping
|
||||
values, the damping effect will vary with the time step. This is not an issue
|
||||
if you use a fixed time step (recommended).
|
||||
|
||||
### Gravity Scale
|
||||
|
||||
You can use the gravity scale to adjust the gravity on a single body. Be
|
||||
careful though, increased gravity can decrease stability.
|
||||
|
||||
`// Set the gravity scale to zero so this body will
|
||||
float`<br/>
|
||||
`bodyDef.gravityScale = 0.0f;`<br/>
|
||||
|
||||
### Sleep Parameters
|
||||
|
||||
What does sleep mean? Well it is expensive to simulate bodies, so the less we
|
||||
have to simulate the better. When a body comes to rest we would like to stop
|
||||
simulating it.
|
||||
|
||||
When LiquidFun determines that a body (or group of bodies) has come to rest,
|
||||
the body enters a sleep state which has very little CPU overhead. If a body is
|
||||
awake and collides with a sleeping body, then the sleeping body wakes up.
|
||||
Bodies will also wake up if a joint or contact attached to them is destroyed.
|
||||
You can also wake a body manually.
|
||||
|
||||
The body definition lets you specify whether a body can sleep and whether a
|
||||
body is created sleeping.
|
||||
|
||||
`bodyDef.allowSleep = true;`<br/>
|
||||
`bodyDef.awake = true;`<br/>
|
||||
|
||||
### Fixed Rotation
|
||||
|
||||
You may want a rigid body, such as a character, to have a fixed rotation. Such
|
||||
a body should not rotate, even under load. You can use the fixed rotation
|
||||
setting to achieve this:
|
||||
|
||||
`bodyDef.fixedRotation = true;`<br/>
|
||||
|
||||
The fixed rotation flag causes the rotational inertia and its inverse to be
|
||||
set to zero.
|
||||
|
||||
### Bullets
|
||||
|
||||
Game simulation usually generates a sequence of images that are played at some
|
||||
frame rate. This is called discrete simulation. In discrete simulation, rigid
|
||||
bodies can move by a large amount in one time step. If a physics engine
|
||||
doesn't account for the large motion, you may see some objects incorrectly
|
||||
pass through each other. This effect is called tunneling.
|
||||
|
||||
By default, LiquidFun uses continuous collision detection (CCD) to prevent
|
||||
dynamic bodies from tunneling through static bodies. This is done by sweeping
|
||||
shapes from their old position to their new positions. The engine looks for
|
||||
new collisions during the sweep and computes the time of impact (TOI) for
|
||||
these collisions. Bodies are moved to their first TOI and then halted for the
|
||||
remainder of the time step.
|
||||
|
||||
Normally CCD is not used between dynamic bodies. This is done to keep
|
||||
performance reasonable. In some game scenarios you need dynamic bodies to use
|
||||
CCD. For example, you may want to shoot a high speed bullet at a stack of
|
||||
dynamic bricks. Without CCD, the bullet might tunnel through the bricks.
|
||||
|
||||
Fast moving objects in LiquidFun can be labeled as bullets. Bullets will
|
||||
perform CCD with both static and dynamic bodies. You should decide what bodies
|
||||
should be bullets based on your game design. If you decide a body should be
|
||||
treated as a bullet, use the following setting.
|
||||
|
||||
`bodyDef.bullet = true;`<br/>
|
||||
|
||||
The bullet flag only affects dynamic bodies.
|
||||
|
||||
LiquidFun performs continuous collision sequentially, so bullets may miss fast
|
||||
moving bodies.
|
||||
|
||||
### Activation
|
||||
|
||||
You may wish a body to be created but not participate in collision or
|
||||
dynamics. This state is similar to sleeping except the body will not be woken
|
||||
by other bodies and the body's fixtures will not be placed in the broad-phase.
|
||||
This means the body will not participate in collisions, ray casts, etc.
|
||||
|
||||
You can create a body in an inactive state and later re-activate it.
|
||||
|
||||
`bodyDef.active = true;`<br/>
|
||||
|
||||
Joints may be connected to inactive bodies. These joints will not be
|
||||
simulated. You should be careful when you activate a body that its joints are
|
||||
not distorted.
|
||||
|
||||
### User Data
|
||||
|
||||
User data is a void pointer. This gives you a hook to link your application
|
||||
objects to bodies. You should be consistent to use the same object type for
|
||||
all body user data.
|
||||
|
||||
`b2BodyDef bodyDef;`<br/>
|
||||
`bodyDef.userData = &myActor;`<br/>
|
||||
|
||||
<a name="bf">
|
||||
## Body Factory
|
||||
|
||||
Bodies are created and destroyed using a body factory provided by the world
|
||||
class. This lets the world create the body with an efficient allocator and add
|
||||
the body to the world data structure.
|
||||
|
||||
Bodies can be dynamic or static depending on the mass properties. Both body
|
||||
types use the same creation and destruction methods.
|
||||
|
||||
`b2Body* dynamicBody = myWorld->CreateBody(&bodyDef);`<br/>
|
||||
`... do stuff …`<br/>
|
||||
`myWorld->DestroyBody(dynamicBody);`<br/>
|
||||
`dynamicBody = NULL;`<br/>
|
||||
|
||||
Caution
|
||||
|
||||
You should never use new or malloc to create a body. The world won't
|
||||
know about the body and the body won't be properly initialized.
|
||||
|
||||
|
||||
|
||||
Static bodies do not move under the influence of other bodies. You may
|
||||
manually move static bodies, but you should be careful so that you don't
|
||||
squash dynamic bodies between two or more static bodies. Friction will not
|
||||
work correctly if you move a static body. Static bodies never collide with
|
||||
static or kinematic bodies. It is faster to attach several shapes to a static
|
||||
body than to create several static bodies with a single shape on each one.
|
||||
Internally, LiquidFun sets the mass and inverse mass of static bodies to zero.
|
||||
This makes the math work out so that most algorithms don't need to treat
|
||||
static bodies as a special case.
|
||||
|
||||
LiquidFun does not keep a reference to the body definition or any of the data
|
||||
it holds (except user data pointers). So you can create temporary body
|
||||
definitions and reuse the same body definitions.
|
||||
|
||||
LiquidFun allows you to avoid destroying bodies by deleting your b2World
|
||||
object, which does all the cleanup work for you. However, you should be
|
||||
mindful to nullify body pointers that you keep in your game engine.
|
||||
|
||||
When you destroy a body, the attached fixtures and joints are automatically
|
||||
destroyed. This has important implications for how you manage shape and joint
|
||||
pointers.
|
||||
|
||||
<a name="ub">
|
||||
## Using a Body
|
||||
|
||||
After creating a body, there are many operations you can perform on the body.
|
||||
These include setting mass properties, accessing position and velocity,
|
||||
applying forces, and transforming points and vectors.
|
||||
|
||||
### Mass Data
|
||||
|
||||
Every body has a mass (scalar), center of mass (2-vector), and rotational
|
||||
inertia (scalar). For static bodies, the mass and rotational inertia are set
|
||||
to zero. When a body has fixed rotation, its rotational inertia is zero.
|
||||
|
||||
Normally the mass properties of a body are established automatically when
|
||||
fixtures are added to the body. You can also adjust the mass of a body at
|
||||
run-time. This is usually done when you have special game scenarios that
|
||||
require altering the mass.
|
||||
|
||||
`void SetMassData(const b2MassData* data);`<br/>
|
||||
|
||||
After setting a body's mass directly, you may wish to revert to the natural
|
||||
mass dictated by the fixtures. You can do this with:
|
||||
|
||||
`void ResetMassData();`<br/>
|
||||
|
||||
The body's mass data is available through the following functions:
|
||||
|
||||
`float32 GetMass() const;`
|
||||
|
||||
`float32 GetInertia() const;`
|
||||
|
||||
`const b2Vec2& GetLocalCenter() const;`
|
||||
|
||||
`void GetMassData(b2MassData* data) const;`
|
||||
|
||||
### State Information
|
||||
|
||||
There are many aspects to the body's state. You can access this state data
|
||||
efficiently through the following functions:
|
||||
|
||||
`void SetType(b2BodyType type);`
|
||||
|
||||
`b2BodyType GetType();`
|
||||
|
||||
`void SetBullet(bool flag);`
|
||||
|
||||
`bool IsBullet() const;`
|
||||
|
||||
`void SetSleepingAllowed(bool flag);`
|
||||
|
||||
`bool IsSleepingAllowed() const;`
|
||||
|
||||
`void SetAwake(bool flag);`
|
||||
|
||||
`bool IsAwake() const;`
|
||||
|
||||
`void SetActive(bool flag);`
|
||||
|
||||
`bool IsActive() const;`
|
||||
|
||||
`void SetFixedRotation(bool flag);`
|
||||
|
||||
`bool IsFixedRotation() const;`
|
||||
|
||||
### Position and Velocity
|
||||
|
||||
You can access the position and rotation of a body. This is common when
|
||||
rendering your associated game actor. You can also set the position, although
|
||||
this is less common since you will normally use LiquidFun to simulate movement.
|
||||
|
||||
`bool SetTransform(const b2Vec2& position, float32
|
||||
angle);`<br/>
|
||||
`const b2Transform& GetTransform() const;`<br/>
|
||||
`const b2Vec2& GetPosition() const;`<br/>
|
||||
`float32 GetAngle() const;`<br/>
|
||||
|
||||
You can access the center of mass position in local and world coordinates.
|
||||
Much of the internal simulation in LiquidFun uses the center of mass. However,
|
||||
you should normally not need to access it. Instead you will usually work with
|
||||
the body transform. For example, you may have a body that is square. The body
|
||||
origin might be a corner of the square, while the center of mass is located at
|
||||
the center of the square.
|
||||
|
||||
`const b2Vec2& GetWorldCenter() const;`<br/>
|
||||
`const b2Vec2& GetLocalCenter() const;`<br/>
|
||||
|
||||
You can access the linear and angular velocity. The linear velocity is for the
|
||||
center of mass. Therefore, the linear velocity may change if the mass
|
||||
properties change.
|
||||
|
||||
|
||||
*This content is licensed under
|
||||
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
|
||||
For details and restrictions, please see the
|
||||
[Content License](md__content_license.html).*
|
||||
@@ -0,0 +1,201 @@
|
||||
# Fixtures
|
||||
|
||||
[About](#about)<br/>
|
||||
[Fixture Creation](#fc)<br/>
|
||||
[Sensors](#se)<br/>
|
||||
|
||||
<a name="about">
|
||||
## About
|
||||
|
||||
Recall that shapes don’t know about bodies and may be used independently of
|
||||
the physics simulation. Therefore LiquidFun provides the b2Fixture class to
|
||||
attach shapes to bodies. A body may have zero or more fixtures. A body with
|
||||
multiple fixtures is sometimes called a *compound body.*
|
||||
|
||||
Fixtures hold the following:
|
||||
|
||||
* a single shape
|
||||
|
||||
* broad-phase proxies
|
||||
|
||||
* density, friction, and restitution
|
||||
|
||||
* collision filtering flags
|
||||
|
||||
* back pointer to the parent body
|
||||
|
||||
* user data
|
||||
|
||||
* sensor flag
|
||||
|
||||
These are described in the following sections.
|
||||
|
||||
<a name="fc">
|
||||
## Fixture Creation
|
||||
|
||||
Fixtures are created by initializing a fixture definition and then passing the
|
||||
definition to the parent body.
|
||||
|
||||
`b2FixtureDef fixtureDef;`<br/>
|
||||
`fixtureDef.shape = &myShape;`<br/>
|
||||
`fixtureDef.density = 1.0f;`<br/>
|
||||
`b2Fixture* myFixture =
|
||||
myBody->CreateFixture(&fixtureDef);`<br/>
|
||||
|
||||
This creates the fixture and attaches it to the body. You do not need to store
|
||||
the fixture pointer since the fixture will automatically be destroyed when the
|
||||
parent body is destroyed. You can create multiple fixtures on a single body.
|
||||
|
||||
You can destroy a fixture on the parent body. You may do this to model a
|
||||
breakable object. Otherwise you can just leave the fixture alone and let the
|
||||
body destruction take care of destroying the attached fixtures.
|
||||
|
||||
`myBody->DestroyFixture(myFixture);`<br/>
|
||||
|
||||
### Density
|
||||
|
||||
The fixture density is used to compute the mass properties of the parent body.
|
||||
The density can be zero or positive. You should generally use similar
|
||||
densities for all your fixtures. This will improve stacking stability.
|
||||
|
||||
The mass of a body is not adjusted when you set the density. You must call
|
||||
ResetMassData for this to occur.
|
||||
|
||||
`fixture->SetDensity(5.0f);`<br/>
|
||||
`body->ResetMassData();`<br/>
|
||||
|
||||
### Friction
|
||||
|
||||
Friction is used to make objects slide along each other realistically.
|
||||
LiquidFun supports static and dynamic friction, but uses the same parameter
|
||||
for both. Friction is simulated accurately in LiquidFun and the friction
|
||||
strength is proportional to the normal force (this is called Coulomb
|
||||
friction). The friction parameter is usually set between 0 and 1, but can be
|
||||
any non-negative value. A friction value of 0 turns off friction and a value
|
||||
of 1 makes the friction strong. When the friction force is computed between
|
||||
two shapes, LiquidFun must combine the friction parameters of the two parent
|
||||
fixtures. This is done with the geometric mean:
|
||||
|
||||
`float32 friction;`<br/>
|
||||
`friction = sqrtf(fixtureA->friction *
|
||||
fixtureB->friction);`<br/>
|
||||
|
||||
So if one fixture has zero friction then the contact will have zero friction.
|
||||
|
||||
You can override the default mixed friction using b2Contact::SetFriction. This
|
||||
is usually done in the b2ContactListener callback.
|
||||
|
||||
### Restitution
|
||||
|
||||
Restitution is used to make objects bounce. The restitution value is usually
|
||||
set to be between 0 and 1. Consider dropping a ball on a table. A value of
|
||||
zero means the ball won't bounce. This is called an inelastic collision. A
|
||||
value of one means the ball's velocity will be exactly reflected. This is
|
||||
called a perfectly elastic collision. Restitution is combined using the
|
||||
following formula.
|
||||
|
||||
`float32 restitution;`<br/>
|
||||
`restitution = b2Max(fixtureA->restitution,
|
||||
fixtureB->restitution);`<br/>
|
||||
|
||||
Restitution is combined this way so that you can have a bouncy super ball
|
||||
without having a bouncy floor.
|
||||
|
||||
You can override the default mixed restitution using
|
||||
b2Contact::SetRestitution. This is usually done in the b2ContactListener
|
||||
callback.
|
||||
|
||||
When a shape develops multiple contacts, restitution is simulated
|
||||
approximately. This is because LiquidFun uses an iterative solver. LiquidFun
|
||||
also uses inelastic collisions when the collision velocity is small. This is
|
||||
done to prevent jitter. See b2_velocityThreshold in b2Settings.h.
|
||||
|
||||
### Filtering
|
||||
|
||||
Collision filtering allows you to prevent collision between fixtures. For
|
||||
example, say you make a character that rides a bicycle. You want the bicycle
|
||||
to collide with the terrain and the character to collide with the terrain, but
|
||||
you don't want the character to collide with the bicycle (because they must
|
||||
overlap). LiquidFun supports such collision filtering using categories and
|
||||
groups.
|
||||
|
||||
LiquidFun supports 16 collision categories. For each fixture you can specify
|
||||
which category it belongs to. You also specify what other categories this
|
||||
fixture can collide with. For example, you could specify in a multiplayer game
|
||||
that all players don't collide with each other and monsters don't collide with
|
||||
each other, but players and monsters should collide. This is done with masking
|
||||
bits. For example:
|
||||
|
||||
`playerFixtureDef.filter.categoryBits = 0x0002;`<br/>
|
||||
`monsterFixtureDef.filter.categoryBits = 0x0004;`<br/>
|
||||
`playerFixtureDef.filter.maskBits = 0x0004;`<br/>
|
||||
`monsterFixtureDef.filter.maskBits = 0x0002;`<br/>
|
||||
|
||||
Here is the rule for a collision to occur:
|
||||
|
||||
`uint16 catA = fixtureA.filter.categoryBits;`<br/>
|
||||
`uint16 maskA = fixtureA.filter.maskBits;`<br/>
|
||||
`uint16 catB = fixtureB.filter.categoryBits;`<br/>
|
||||
`uint16 maskB = fixtureB.filter.maskBits;`<br/>
|
||||
`if ((catA & maskB) != 0 && (catB & maskA) != 0)`<br/>
|
||||
`{`<br/>
|
||||
`// fixtures can collide`<br/>
|
||||
`}`<br/>
|
||||
|
||||
Collision groups let you specify an integral group index. You can have all
|
||||
fixtures with the same group index always collide (positive index) or never
|
||||
collide (negative index). Group indices are usually used for things that are
|
||||
somehow related, like the parts of a bicycle. In the following example,
|
||||
fixture1 and fixture2 always collide, but fixture3 and fixture4 never collide.
|
||||
|
||||
`fixture1Def.filter.groupIndex = 2;`<br/>
|
||||
`fixture2Def.filter.groupIndex = 2;`<br/>
|
||||
`fixture3Def.filter.groupIndex = -8;`<br/>
|
||||
`fixture4Def.filter.groupIndex = -8;`<br/>
|
||||
|
||||
Collisions between fixtures of different group indices are filtered according
|
||||
the category and mask bits. In other words, group filtering has higher
|
||||
precedence than category filtering.
|
||||
|
||||
Note that additional collision filtering occurs in LiquidFun. Here is a list:
|
||||
|
||||
* A fixture on a static body can only collide with a dynamic body.
|
||||
|
||||
* A fixture on a kinematic body can only collide with a dynamic body.
|
||||
|
||||
* Fixtures on the same body never collide with each other.
|
||||
|
||||
* You can optionally enable/disable collision between fixtures on bodies
|
||||
connected by a joint.
|
||||
|
||||
Sometimes you might need to change collision filtering after a fixture has
|
||||
already been created. You can get and set the b2Filter structure on an
|
||||
existing fixture using b2Fixture::GetFilterData and b2Fixture::SetFilterData.
|
||||
Note that changing the filter data will not add or remove contacts until the
|
||||
next time step (see the World class).
|
||||
|
||||
<a name="se">
|
||||
## Sensors
|
||||
|
||||
Sometimes game logic needs to know when two fixtures overlap yet there should
|
||||
be no collision response. This is done by using sensors. A sensor is a fixture
|
||||
that detects collision but does not produce a response.
|
||||
|
||||
You can flag any fixture as being a sensor. Sensors may be static, kinematic,
|
||||
or dynamic. Remember that you may have multiple fixtures per body and you can
|
||||
have any mix of sensors and solid fixtures. Also, sensors only form contacts
|
||||
when at least one body is dynamic, so you will not get a contact for kinematic
|
||||
versus kinematic, kinematic versus static, or static versus static.
|
||||
|
||||
Sensors do not generate contact points. There are two ways to get the state of
|
||||
a sensor:
|
||||
|
||||
1. `b2Contact::IsTouching`
|
||||
|
||||
2. `b2ContactListener::BeginContact and EndContact`
|
||||
|
||||
|
||||
*This content is licensed under
|
||||
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
|
||||
For details and restrictions, please see the
|
||||
[Content License](md__content_license.html).*
|
||||
@@ -0,0 +1,434 @@
|
||||
# Joints
|
||||
|
||||
[About](#about)<br/>
|
||||
[Joint Definition](#jd)<br/>
|
||||
[Joint Factory](#jf)<br/>
|
||||
[Using Joints](#uj)<br/>
|
||||
|
||||
<a name="about">
|
||||
|
||||
## About
|
||||
|
||||
Joints are used to constrain bodies to the world or to each other. Typical
|
||||
examples in games include ragdolls, teeters, and pulleys. Joints can be
|
||||
combined in many different ways to create interesting motions.
|
||||
|
||||
Some joints provide limits so you can control the range of motion. Some joint
|
||||
provide motors which can be used to drive the joint at a prescribed speed
|
||||
until a prescribed force/torque is exceeded.
|
||||
|
||||
Joint motors can be used in many ways. You can use motors to control position
|
||||
by specifying a joint velocity that is proportional to the difference between
|
||||
the actual and desired position. You can also use motors to simulate joint
|
||||
friction: set the joint velocity to zero and provide a small, but significant
|
||||
maximum motor force/torque. Then the motor will attempt to keep the joint from
|
||||
moving until the load becomes too strong.
|
||||
|
||||
<a name="jd">
|
||||
## Joint Definition
|
||||
|
||||
Each joint type has a definition that derives from b2JointDef. All joints are
|
||||
connected between two different bodies. One body may static. Joints between
|
||||
static and/or kinematic bodies are allowed, but have no effect and use some
|
||||
processing time.
|
||||
|
||||
You can specify user data for any joint type and you can provide a flag to
|
||||
prevent the attached bodies from colliding with each other. This is actually
|
||||
the default behavior and you must set the collideConnected Boolean to allow
|
||||
collision between to connected bodies.
|
||||
|
||||
Many joint definitions require that you provide some geometric data. Often a
|
||||
joint will be defined by anchor points. These are points fixed in the attached
|
||||
bodies. LiquidFun requires these points to be specified in local coordinates.
|
||||
This way the joint can be specified even when the current body transforms
|
||||
violate the joint constraint --- a common occurrence when a game is saved and
|
||||
reloaded. Additionally, some joint definitions need to know the default
|
||||
relative angle between the bodies. This is necessary to constrain rotation
|
||||
correctly.
|
||||
|
||||
Initializing the geometric data can be tedious, so many joints have
|
||||
initialization functions that use the current body transforms to remove much
|
||||
of the work. However, these initialization functions should usually only be
|
||||
used for prototyping. Production code should define the geometry directly.
|
||||
This will make joint behavior more robust.
|
||||
|
||||
The rest of the joint definition data depends on the joint type. We cover
|
||||
these now.
|
||||
|
||||
<a name="jf">
|
||||
## Joint Factory
|
||||
|
||||
Joints are created and destroyed using the world factory methods. This brings
|
||||
up an old issue:
|
||||
|
||||
Caution
|
||||
|
||||
Don't try to create a joint on the stack or on the heap using new or malloc. You must create and destroy bodies and joints using the create and destroy methods of the b2World class.
|
||||
|
||||
Here's an example of the lifetime of a revolute joint:
|
||||
|
||||
`b2RevoluteJointDef jointDef;`<br/>
|
||||
`jointDef.bodyA = myBodyA;`<br/>
|
||||
`jointDef.bodyB = myBodyB;`<br/>
|
||||
`jointDef.anchorPoint = myBodyA->GetCenterPosition();`<br/>
|
||||
`b2RevoluteJoint* joint =
|
||||
(b2RevoluteJoint*)myWorld->CreateJoint(&jointDef);`<br/>
|
||||
`… do stuff …`<br/>
|
||||
`myWorld->DestroyJoint(joint);`<br/>
|
||||
`joint = NULL;`<br/>
|
||||
|
||||
It is always good to nullify your pointer after they are destroyed. This will
|
||||
make the program crash in a controlled manner if you try to reuse the pointer.
|
||||
|
||||
The lifetime of a joint is not simple. Heed this warning well:
|
||||
|
||||
Caution
|
||||
|
||||
Joints are destroyed when an attached body is destroyed.
|
||||
|
||||
This precaution is not always necessary. You may organize your game engine so
|
||||
that joints are always destroyed before the attached bodies. In this case you
|
||||
don't need to implement the listener class. See the section on Implicit
|
||||
Destruction for details.
|
||||
|
||||
<a name="uj">
|
||||
## Using Joints
|
||||
|
||||
Many simulations create the joints and don't access them again until they are
|
||||
destroyed. However, there is a lot of useful data contained in joints that you
|
||||
can use to create a rich simulation.
|
||||
|
||||
First of all, you can get the bodies, anchor points, and user data from a
|
||||
joint.
|
||||
|
||||
`b2Body* GetBodyA();`
|
||||
|
||||
`b2Body* GetBodyB();`
|
||||
|
||||
`b2Vec2 GetAnchorA();`
|
||||
|
||||
`b2Vec2 GetAnchorB();`
|
||||
|
||||
`void* GetUserData();`
|
||||
|
||||
All joints have a reaction force and torque. This the reaction force applied
|
||||
to body 2 at the anchor point. You can use reaction forces to break joints or
|
||||
trigger other game events. These functions may do some computations, so don't
|
||||
call them if you don't need the result.
|
||||
|
||||
`b2Vec2 GetReactionForce();`<br/>
|
||||
|
||||
`float32 GetReactionTorque();`<br/>
|
||||
|
||||
### Distance Joint
|
||||
|
||||
One of the simplest joint is a distance joint which says that the distance
|
||||
between two points on two bodies must be constant. When you specify a distance
|
||||
joint the two bodies should already be in place. Then you specify the two
|
||||
anchor points in world coordinates. The first anchor point is connected to
|
||||
body 1, and the second anchor point is connected to body 2. These points imply
|
||||
the length of the distance constraint.
|
||||
|
||||
<img align="center" src="image_18.gif" alt="Distance joint" height="118"
|
||||
width="155"><br/>
|
||||
|
||||
Here is an example of a distance joint definition. In this case we decide to
|
||||
allow the bodies to collide.
|
||||
|
||||
`b2DistanceJointDef jointDef;`<br/>
|
||||
`jointDef.Initialize(myBodyA, myBodyB, worldAnchorOnBodyA,
|
||||
worldAnchorOnBodyB);`<br/>
|
||||
`jointDef.collideConnected = true;`<br/>
|
||||
|
||||
The distance joint can also be made soft, like a spring-damper connection. See
|
||||
the Web example in the testbed to see how this behaves.
|
||||
|
||||
Softness is achieved by tuning two constants in the definition: frequency and
|
||||
damping ratio. Think of the frequency as the frequency of a harmonic
|
||||
oscillator (like a guitar string). The frequency is specified in Hertz.
|
||||
Typically the frequency should be less than a half the frequency of the time
|
||||
step. So if you are using a 60Hz time step, the frequency of the distance
|
||||
joint should be less than 30Hz. The reason is related to the Nyquist frequency.
|
||||
|
||||
The damping ratio is non-dimensional and is typically between 0 and 1, but can
|
||||
be larger. At 1, the damping is critical (all oscillations should vanish).
|
||||
|
||||
`jointDef.frequencyHz = 4.0f;`<br/>
|
||||
`jointDef.dampingRatio = 0.5f;`<br/>
|
||||
|
||||
### Revolute Joint
|
||||
|
||||
A revolute joint forces two bodies to share a common anchor point, often
|
||||
called a hinge point. The revolute joint has a single degree of freedom: the
|
||||
relative rotation of the two bodies. This is called the joint angle.
|
||||
|
||||
<img align="center" src="image_19.gif" alt="Revolute joint" height="97"
|
||||
width="139"><br/>
|
||||
|
||||
To specify a revolute you need to provide two bodies and a single anchor point
|
||||
in world space. The initialization function assumes that the bodies are
|
||||
already in the correct position.
|
||||
|
||||
In this example, two bodies are connected by a revolute joint at the first
|
||||
body's center of mass.
|
||||
|
||||
`b2RevoluteJointDef jointDef;`<br/>
|
||||
`jointDef.Initialize(myBodyA, myBodyB,
|
||||
myBodyA->GetWorldCenter());`<br/>
|
||||
|
||||
The revolute joint angle is positive when bodyB rotates CCW about the angle
|
||||
point. Like all angles in LiquidFun, the revolute angle is measured in
|
||||
radians. By convention the revolute joint angle is zero when the joint is
|
||||
created using Initialize(), regardless of the current rotation of the two
|
||||
bodies.
|
||||
|
||||
In some cases you might wish to control the joint angle. For this, the
|
||||
revolute joint can optionally simulate a joint limit and/or a motor.
|
||||
|
||||
A joint limit forces the joint angle to remain between a lower and upper
|
||||
bound. The limit will apply as much torque as needed to make this happen. The
|
||||
limit range should include zero, otherwise the joint will lurch when the
|
||||
simulation begins.
|
||||
|
||||
A joint motor allows you to specify the joint speed (the time derivative of
|
||||
the angle). The speed can be negative or positive. A motor can have infinite
|
||||
force, but this is usually not desirable. Recall the eternal question:
|
||||
|
||||
*"What happens when an irresistible force meets an immovable object?"*
|
||||
|
||||
I can tell you it's not pretty. So you can provide a maximum torque for the
|
||||
joint motor. The joint motor will maintain the specified speed unless the
|
||||
required torque exceeds the specified maximum. When the maximum torque is
|
||||
exceeded, the joint will slow down and can even reverse.
|
||||
|
||||
You can use a joint motor to simulate joint friction. Just set the joint speed
|
||||
to zero, and set the maximum torque to some small, but significant value. The
|
||||
motor will try to prevent the joint from rotating, but will yield to a
|
||||
significant load.
|
||||
|
||||
Here's a revision of the revolute joint definition above; this time the joint
|
||||
has a limit and a motor enabled. The motor is setup to simulate joint friction.
|
||||
|
||||
b2RevoluteJointDef jointDef;
|
||||
jointDef.Initialize(bodyA, bodyB, myBodyA->GetWorldCenter());
|
||||
jointDef.lowerAngle = -0.5f * b2_pi; // -90 degrees
|
||||
jointDef.upperAngle = 0.25f * b2_pi; // 45 degrees
|
||||
jointDef.enableLimit = true;
|
||||
jointDef.maxMotorTorque = 10.0f;
|
||||
jointDef.motorSpeed = 0.0f;
|
||||
jointDef.enableMotor = true;
|
||||
|
||||
You can access a revolute joint's angle, speed, and motor torque.
|
||||
|
||||
`float32 GetJointAngle() const;`<br/>
|
||||
`float32 GetJointSpeed() const;`<br/>
|
||||
`float32 GetMotorTorque() const;`<br/>
|
||||
|
||||
You also update the motor parameters each step.
|
||||
|
||||
`void SetMotorSpeed(float32 speed);`<br/>
|
||||
`void SetMaxMotorTorque(float32 torque);`<br/>
|
||||
|
||||
Joint motors have some interesting abilities. You can update the joint speed
|
||||
every time step so you can make the joint move back-and-forth like a sine-wave
|
||||
or according to whatever function you want.
|
||||
|
||||
`... Game Loop Begin ...`<br/>
|
||||
`myJoint->SetMotorSpeed(cosf(0.5f * time));`<br/>
|
||||
`... Game Loop End ...`<br/>
|
||||
|
||||
You can also use joint motors to track a desired joint angle. For example:
|
||||
|
||||
`... Game Loop Begin ...`<br/>
|
||||
`float32 angleError = myJoint->GetJointAngle() -
|
||||
angleTarget;`<br/>
|
||||
`float32 gain = 0.1f;`<br/>
|
||||
`myJoint->SetMotorSpeed(-gain * angleError);`<br/>
|
||||
`... Game Loop End ...`<br/>
|
||||
|
||||
Generally your gain parameter should not be too large. Otherwise your joint
|
||||
may become unstable.
|
||||
|
||||
### Prismatic Joint
|
||||
|
||||
A prismatic joint allows for relative translation of two bodies along a
|
||||
specified axis. A prismatic joint prevents relative rotation. Therefore, a
|
||||
prismatic joint has a single degree of freedom.
|
||||
|
||||
<img align="center" src="image_20.gif" alt="Prismatic joint" height="134"
|
||||
width="165"><br/>
|
||||
|
||||
The prismatic joint definition is similar to the revolute joint description;
|
||||
just substitute translation for angle and force for torque. Using this analogy
|
||||
provides an example prismatic joint definition with a joint limit and a
|
||||
friction motor:
|
||||
|
||||
`b2PrismaticJointDef jointDef;`<br/>
|
||||
`b2Vec2 worldAxis(1.0f, 0.0f);`<br/>
|
||||
`jointDef.Initialize(myBodyA, myBodyB,
|
||||
myBodyA->GetWorldCenter(), worldAxis);`<br/>
|
||||
`jointDef.lowerTranslation = -5.0f;`<br/>
|
||||
`jointDef.upperTranslation = 2.5f;`<br/>
|
||||
`jointDef.enableLimit = true;`<br/>
|
||||
`jointDef.maxMotorForce = 1.0f;`<br/>
|
||||
`jointDef.motorSpeed = 0.0f;`<br/>
|
||||
`jointDef.enableMotor = true;`<br/>
|
||||
|
||||
The revolute joint has an implicit axis coming out of the screen. The
|
||||
prismatic joint needs an explicit axis parallel to the screen. This axis is
|
||||
fixed in the two bodies and follows their motion.
|
||||
|
||||
Like the revolute joint, the prismatic joint translation is zero when the
|
||||
joint is created using Initialize(). So be sure zero is between your lower and
|
||||
upper translation limits.
|
||||
|
||||
Using a prismatic joint is similar to using a revolute joint. Here are the
|
||||
relevant member functions:
|
||||
|
||||
`float32 GetJointTranslation() const;`
|
||||
|
||||
`float32 GetJointSpeed() const;`
|
||||
|
||||
`float32 GetMotorForce() const;`
|
||||
|
||||
`void SetMotorSpeed(float32 speed);`
|
||||
|
||||
`void SetMotorForce(float32 force);`
|
||||
|
||||
### Pulley Joint
|
||||
|
||||
A pulley is used to create an idealized pulley. The pulley connects two bodies
|
||||
to ground and to each other. As one body goes up, the other goes down. The
|
||||
total length of the pulley rope is conserved according to the initial
|
||||
configuration.
|
||||
|
||||
length1 + length2 == constant
|
||||
|
||||
You can supply a ratio that simulates a block and tackle. This causes one side
|
||||
of the pulley to extend faster than the other. At the same time the constraint
|
||||
force is smaller on one side than the other. You can use this to create
|
||||
mechanical leverage.
|
||||
|
||||
length1 + ratio * length2 == constant
|
||||
|
||||
For example, if the ratio is 2, then length1 will vary at twice the rate of
|
||||
length2. Also the force in the rope attached to body1 will have half the
|
||||
constraint force as the rope attached to body2.
|
||||
|
||||
<img align="center" src="image_21.gif" alt="Pulley joint" height="213"
|
||||
width="237"><br/>
|
||||
|
||||
Pulleys can be troublesome when one side is fully extended. The rope on the
|
||||
other side will have zero length. At this point the constraint equations
|
||||
become singular (bad). You should configure collision shapes to prevent this.
|
||||
|
||||
Here is an example pulley definition:
|
||||
|
||||
`b2Vec2 anchor1 = myBody1->GetWorldCenter();`<br/>
|
||||
`b2Vec2 anchor2 = myBody2->GetWorldCenter();`<br/>
|
||||
`b2Vec2 groundAnchor1(p1.x, p1.y + 10.0f);`<br/>
|
||||
`b2Vec2 groundAnchor2(p2.x, p2.y + 12.0f);`<br/>
|
||||
`float32 ratio = 1.0f;`<br/>
|
||||
`b2PulleyJointDef jointDef;`<br/>
|
||||
`jointDef.Initialize(myBody1, myBody2, groundAnchor1,
|
||||
groundAnchor2, anchor1, anchor2, ratio);`<br/>
|
||||
|
||||
Pulley joints provide the current lengths.
|
||||
|
||||
`float32 GetLengthA() const;`<br/>
|
||||
`float32 GetLengthB() const;`<br/>
|
||||
|
||||
### Gear Joint
|
||||
|
||||
If you want to create a sophisticated mechanical contraption you might want to
|
||||
use gears. In principle you can create gears in LiquidFun by using compound
|
||||
shapes to model gear teeth. This is not very efficient and might be tedious to
|
||||
author. You also have to be careful to line up the gears so the teeth mesh
|
||||
smoothly. LiquidFun has a simpler method of creating gears: the gear joint.
|
||||
|
||||
<img align="center" src="image_22.gif" alt="Gear joint" height="188"
|
||||
width="179"><br/>
|
||||
|
||||
The gear joint can only connect revolute and/or prismatic joints.
|
||||
|
||||
Like the pulley ratio, you can specify a gear ratio. However, in this case the
|
||||
gear ratio can be negative. Also keep in mind that when one joint is a
|
||||
revolute joint (angular) and the other joint is prismatic (translation), and
|
||||
then the gear ratio will have units of length or one over length.
|
||||
|
||||
coordinate1 + ratio * coordinate2 == constant
|
||||
|
||||
Here is an example gear joint. The bodies myBodyA and myBodyB are any bodies
|
||||
from the two joints, as long as they are not the same bodies.
|
||||
|
||||
`b2GearJointDef jointDef;`<br/>
|
||||
`jointDef.bodyA = myBodyA;`<br/>
|
||||
`jointDef.bodyB = myBodyB;`<br/>
|
||||
`jointDef.joint1 = myRevoluteJoint;`<br/>
|
||||
`jointDef.joint2 = myPrismaticJoint;`<br/>
|
||||
`jointDef.ratio = 2.0f * b2_pi / myLength;`<br/>
|
||||
|
||||
Note that the gear joint depends on two other joints. This creates a fragile
|
||||
situation. What happens if those joints are deleted?
|
||||
|
||||
Caution
|
||||
|
||||
Always delete gear joints before the revolute/prismatic joints on the gears. Otherwise your code will crash in a bad way due to the orphaned joint pointers in the gear joint. You should also delete the gear joint before you delete any of the bodies involved.
|
||||
|
||||
|
||||
### Mouse Joint
|
||||
|
||||
The mouse joint is used in the testbed to manipulate bodies with the mouse. It
|
||||
attempts to drive a point on a body towards the current position of the
|
||||
cursor. There is no restriction on rotation.
|
||||
|
||||
The mouse joint definition has a target point, maximum force, frequency, and
|
||||
damping ratio. The target point initially coincides with the body’s anchor
|
||||
point. The maximum force is used to prevent violent reactions when multiple
|
||||
dynamic bodies interact. You can make this as large as you like. The frequency
|
||||
and damping ratio are used to create a spring/damper effect similar to the
|
||||
distance joint.
|
||||
|
||||
Many users have tried to adapt the mouse joint for game play. Users often
|
||||
want to achieve precise positioning and instantaneous response. The mouse
|
||||
joint doesn’t work very well in that context. You may wish to consider using
|
||||
kinematic bodies instead.
|
||||
|
||||
### Wheel Joint
|
||||
|
||||
The wheel joint restricts a point on bodyB to a line on bodyA. The wheel joint
|
||||
also provides a suspension spring. See b2WheelJoint.h and Car.h for details.
|
||||
|
||||
<img align="center" src="image_23.png" alt="Wheel joint" height="286"
|
||||
width="157"><br/>
|
||||
|
||||
### Weld Joint
|
||||
|
||||
The weld joint attempts to constrain all relative motion between two bodies.
|
||||
See the Cantilever.h in the testbed to see how the weld joint behaves.
|
||||
|
||||
It is tempting to use the weld joint to define breakable structures. However,
|
||||
the LiquidFun solver is iterative so the joints are a bit soft. So chains of
|
||||
bodies connected by weld joints will flex.
|
||||
|
||||
Instead it is better to create breakable bodies starting with a single body
|
||||
with multiple fixtures. When the body breaks, you can destroy a fixture and
|
||||
recreate it on a new body. See the Breakable example in the testbed.
|
||||
|
||||
### Rope Joint
|
||||
|
||||
The rope joint restricts the maximum distance between two points. This can be
|
||||
useful to prevent chains of bodies from stretching, even under high load. See
|
||||
b2RopeJoint.h and RopeJoint.h for details.
|
||||
|
||||
### Friction Joint
|
||||
|
||||
The friction joint is used for top-down friction. The joint provides 2D
|
||||
translational friction and angular friction. See b2FrictionJoint.h and
|
||||
ApplyForce.h for details.
|
||||
|
||||
|
||||
*This content is licensed under
|
||||
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
|
||||
For details and restrictions, please see the
|
||||
[Content License](md__content_license.html).*
|
||||
@@ -0,0 +1,364 @@
|
||||
# Contacts
|
||||
|
||||
[About](#about)<br/>
|
||||
[Contact Class](#cc)<br/>
|
||||
[Accessing Contacts](#ac)<br/>
|
||||
[Contact Listener](#cl)<br/>
|
||||
[Contact Filtering](#cf)<br/>
|
||||
|
||||
|
||||
<a name="about"></a>
|
||||
## About
|
||||
|
||||
Contacts are objects created by LiquidFun to manage collision between two
|
||||
fixtures. If the fixture has children, such as a chain shape, then a contact
|
||||
exists for each relevant child. There are different kinds of contacts, derived
|
||||
from b2Contact, for managing contact between different kinds of fixtures. For
|
||||
example there is a contact class for managing polygon-polygon collision and
|
||||
another contact class for managing circle-circle collision.
|
||||
|
||||
Here is some terminology associated with contacts.
|
||||
|
||||
### contact point
|
||||
|
||||
A contact point is a point where two shapes touch. LiquidFun approximates
|
||||
contact with a small number of points.
|
||||
|
||||
### contact normal
|
||||
|
||||
A contact normal is a unit vector that points from one shape to another. By
|
||||
convention, the normal points from fixtureA to fixtureB.
|
||||
|
||||
### contact separation
|
||||
|
||||
Separation is the opposite of penetration. Separation is negative when shapes
|
||||
overlap. It is possible that future versions of LiquidFun will create contact
|
||||
points with positive separation, so you may want to check the sign when
|
||||
contact points are reported.
|
||||
|
||||
### contact manifold
|
||||
|
||||
Contact between two convex polygons may generate up to 2 contact points. Both
|
||||
of these points use the same normal, so they are grouped into a contact
|
||||
manifold, which is an approximation of a continuous region of contact.
|
||||
|
||||
### normal impulse
|
||||
|
||||
The normal force is the force applied at a contact point to prevent the shapes
|
||||
from penetrating. For convenience, LiquidFun works with impulses. The normal
|
||||
impulse is just the normal force multiplied by the time step.
|
||||
|
||||
### tangent impulse
|
||||
|
||||
The tangent force is generated at a contact point to simulate friction. For
|
||||
convenience, this is stored as an impulse.
|
||||
|
||||
### contact ids
|
||||
|
||||
LiquidFun tries to re-use the contact force results from a time step as the
|
||||
initial guess for the next time step. LiquidFun uses contact ids to match
|
||||
contact points across time steps. The ids contain geometric features indices
|
||||
that help to distinguish one contact point from another.
|
||||
|
||||
Contacts are created when two fixture’s AABBs overlap. Sometimes collision
|
||||
filtering will prevent the creation of contacts. Contacts are destroyed with
|
||||
the AABBs cease to overlap.
|
||||
|
||||
So you might gather that there may be contacts created for fixtures that are
|
||||
not touching (just their AABBs). Well, this is correct. It's a "chicken or egg"
|
||||
problem. We don't know if we need a contact object until one is created
|
||||
to analyze the collision. We could delete the contact right away if the shapes
|
||||
are not touching, or we can just wait until the AABBs stop overlapping.
|
||||
LiquidFun takes the latter approach because it lets the system cache
|
||||
information to improve performance.
|
||||
|
||||
<a name="cc">
|
||||
## Contact Class
|
||||
|
||||
As mentioned before, the contact class is created and destroyed by LiquidFun.
|
||||
Contact objects are not created by the user. However, you are able to access
|
||||
the contact class and interact with it.
|
||||
|
||||
You can access the raw contact manifold:
|
||||
|
||||
`b2Manifold* GetManifold();`<br/>
|
||||
`const b2Manifold* GetManifold() const;`<br/>
|
||||
|
||||
You can potentially modify the manifold, but this is generally not supported
|
||||
and is for advanced usage.
|
||||
|
||||
There is a helper function to get the b2WorldManifold:
|
||||
|
||||
`void GetWorldManifold(b2WorldManifold* worldManifold)
|
||||
const;`<br/>
|
||||
|
||||
This uses the current positions of the bodies to compute world positions of
|
||||
the contact points.
|
||||
|
||||
Sensors do not create manifolds, so for them use:
|
||||
|
||||
`bool touching = sensorContact->IsTouching();`<br/>
|
||||
|
||||
This function also works for non-sensors.
|
||||
|
||||
You can get the fixtures from a contact. From those you can get the bodies.
|
||||
|
||||
`b2Fixture* fixtureA = myContact->GetFixtureA();`<br/>
|
||||
`b2Body* bodyA = fixtureA->GetBody();`<br/>
|
||||
`MyActor* actorA = (MyActor*)bodyA->GetUserData();`<br/>
|
||||
|
||||
You can disable a contact. This only works inside the
|
||||
b2ContactListener::PreSolve event, discussed below.
|
||||
|
||||
<a name="ac">
|
||||
## Accessing Contacts
|
||||
|
||||
You can get access to contacts in several ways. You can access the contacts
|
||||
directly on the world and body structures. You can also implement a contact
|
||||
listener.
|
||||
|
||||
You can iterate over all contacts in the world:
|
||||
|
||||
`for (b2Contact* c = myWorld->GetContactList(); c; c =
|
||||
c->GetNext())`<br/>
|
||||
`{`<br/>
|
||||
`// process c`<br/>
|
||||
`}`<br/>
|
||||
|
||||
You can also iterate over all the contacts on a body. These are stored in a
|
||||
graph using a contact edge structure.
|
||||
|
||||
`for (b2ContactEdge* ce = myBody->GetContactList(); ce; ce =
|
||||
ce->next)`<br/>
|
||||
`{`<br/>
|
||||
` b2Contact* c = ce->contact;`<br/>
|
||||
` // process c`<br/>
|
||||
`}`<br/>
|
||||
|
||||
You can also access contacts using the contact listener that is described
|
||||
below.
|
||||
|
||||
Caution
|
||||
|
||||
Accessing contacts off b2World and b2Body may miss some transient
|
||||
contacts that occur in the middle of the time step. Use b2ContactListener to
|
||||
get the most accurate results.
|
||||
|
||||
<a name="cl">
|
||||
## Contact Listener
|
||||
|
||||
You can receive contact data by implementing b2ContactListener. The contact
|
||||
listener supports several events: begin, end, pre-solve, and post-solve.
|
||||
|
||||
`class MyContactListener : public b2ContactListener`<br/>
|
||||
`{`<br/>
|
||||
`public:`<br/>
|
||||
`void BeginContact(b2Contact*
|
||||
contact)`<br/>
|
||||
`{ /* handle begin event */ }`<br/>
|
||||
`void EndContact(b2Contact* contact)`<br/>
|
||||
`{ /* handle end event */ }`<br/>
|
||||
`void PreSolve(b2Contact* contact, const
|
||||
b2Manifold* oldManifold)`<br/>
|
||||
`{ /* handle pre-solve event */ }`<br/>
|
||||
`void PostSolve(b2Contact* contact, const
|
||||
b2ContactImpulse* impulse)`<br/>
|
||||
`{ /* handle post-solve event */ }`<br/>
|
||||
`};`<br/>
|
||||
|
||||
Caution
|
||||
|
||||
Do not keep a reference to the pointers sent to b2ContactListener.
|
||||
Instead make a deep copy of the contact point data into your own buffer. The
|
||||
example below shows one way of doing this.
|
||||
|
||||
At run-time you can create an instance of the listener and register it
|
||||
with b2World::SetContactListener. Be sure your listener remains in scope while
|
||||
the world object exists.
|
||||
|
||||
### Begin Contact Event
|
||||
|
||||
This is called when two fixtures begin to overlap. This is called for sensors
|
||||
and non-sensors. This event can only occur inside the time step.
|
||||
|
||||
### End Contact Event
|
||||
|
||||
This is called when two fixtures cease to overlap. This is called for sensors
|
||||
and non-sensors. This may be called when a body is destroyed, so this event
|
||||
can occur outside the time step.
|
||||
|
||||
### Pre-Solve Event
|
||||
|
||||
This is called after collision detection, but before collision resolution.
|
||||
This gives you a chance to disable the contact based on the current
|
||||
configuration. For example, you can implement a one-sided platform using this
|
||||
callback and calling b2Contact::SetEnabled(false). The contact will be
|
||||
re-enabled each time through collision processing, so you will need to disable
|
||||
the contact every time-step. The pre-solve event may be fired multiple times
|
||||
per time step per contact due to continuous collision detection.
|
||||
|
||||
`void PreSolve(b2Contact* contact, const b2Manifold*
|
||||
oldManifold)`<br/>
|
||||
`{`<br/>
|
||||
`b2WorldManifold worldManifold;`<br/>
|
||||
`contact->GetWorldManifold(&worldManifold);`<br/>
|
||||
`if (worldManifold.normal.y < -0.5f)`<br/>
|
||||
`{`<br/>
|
||||
`contact->SetEnabled(false);`<br/>
|
||||
`}`<br/>
|
||||
`}`<br/>
|
||||
|
||||
The pre-solve event is also a good place to determine the point state and the
|
||||
approach velocity of collisions.
|
||||
|
||||
`void PreSolve(b2Contact* contact, const b2Manifold*
|
||||
oldManifold)`<br/>
|
||||
`{`<br/>
|
||||
`b2WorldManifold worldManifold;`<br/>
|
||||
`contact->GetWorldManifold(&worldManifold);`<br/>
|
||||
`b2PointState state1[2], state2[2];`<br/>
|
||||
`b2GetPointStates(state1, state2,
|
||||
oldManifold, contact->GetManifold());`<br/>
|
||||
`if (state2[0] == b2_addState)`<br/>
|
||||
`{`<br/>
|
||||
`const b2Body* bodyA =
|
||||
contact->GetFixtureA()->GetBody();`<br/>
|
||||
`const b2Body* bodyB =
|
||||
contact->GetFixtureB()->GetBody();`<br/>
|
||||
`b2Vec2 point =
|
||||
worldManifold.points[0];`<br/>
|
||||
`b2Vec2 vA =
|
||||
bodyA->GetLinearVelocityFromWorldPoint(point);`<br/>
|
||||
`b2Vec2 vB =
|
||||
bodyB->GetLinearVelocityFromWorldPoint(point);`<br/>
|
||||
`float32
|
||||
approachVelocity = b2Dot(vB – vA, worldManifold.normal);`<br/>
|
||||
`if (approachVelocity >
|
||||
1.0f)`<br/>
|
||||
`{`<br/>
|
||||
`MyPlayCollisionSound();`<br/>
|
||||
`}`<br/>
|
||||
`}`<br/>
|
||||
`}`<br/>
|
||||
|
||||
### Post-Solve Event
|
||||
|
||||
The post solve event is where you can gather collision impulse results. If you
|
||||
don’t care about the impulses, you should probably just implement the
|
||||
pre-solve event.
|
||||
|
||||
It is tempting to implement game logic that alters the physics world inside a
|
||||
contact callback. For example, you may have a collision that applies damage
|
||||
and try to destroy the associated actor and its rigid body. However, LiquidFun
|
||||
does not allow you to alter the physics world inside a callback because you
|
||||
might destroy objects that LiquidFun is currently processing, leading to
|
||||
orphaned pointers.
|
||||
|
||||
The recommended practice for processing contact points is to buffer all
|
||||
contact data that you care about and process it after the time step. You
|
||||
should always process the contact points immediately after the time step;
|
||||
otherwise some other client code might alter the physics world, invalidating
|
||||
the contact buffer. When you process the contact buffer you can alter the
|
||||
physics world, but you still need to be careful that you don't orphan pointers
|
||||
stored in the contact point buffer. The testbed has example contact point
|
||||
processing that is safe from orphaned pointers.
|
||||
|
||||
This code from the CollisionProcessing test shows how to handle orphaned
|
||||
bodies when processing the contact buffer. Here is an excerpt. Be sure to read
|
||||
the comments in the listing. This code assumes that all contact points have
|
||||
been buffered in the b2ContactPoint array m_points.
|
||||
|
||||
`// We are going to destroy some bodies according to
|
||||
contact`<br/>
|
||||
`// points. We must buffer the bodies that should be
|
||||
destroyed`<br/>
|
||||
`// because they may belong to multiple contact points.`<br/>
|
||||
`const int32 k_maxNuke = 6;`<br/>
|
||||
`b2Body* nuke[k_maxNuke];`<br/>
|
||||
`int32 nukeCount = 0;`<br/>
|
||||
`// Traverse the contact buffer. Destroy bodies that`<br/>
|
||||
`// are touching heavier bodies.`<br/>
|
||||
`for (int32 i = 0; i < m_pointCount; ++i)`<br/>
|
||||
`{`<br/>
|
||||
`ContactPoint* point = m_points + i;`<br/>
|
||||
`b2Body* bodyA =
|
||||
point->fixtureA->GetBody();`<br/>
|
||||
`b2Body* bodyB =
|
||||
point->FixtureB->GetBody();`<br/>
|
||||
`float32 massA = bodyA->GetMass();`<br/>
|
||||
`float32 massB = bodyB->GetMass();`<br/>
|
||||
`if (massA > 0.0f && massB > 0.0f)`<br/>
|
||||
`{`<br/>
|
||||
`if (massB > massA)`<br/>
|
||||
`{`<br/>
|
||||
`nuke[nukeCount++] = bodyA;`<br/>
|
||||
`}`<br/>
|
||||
`else`<br/>
|
||||
`{`<br/>
|
||||
`nuke[nukeCount++] = bodyB;`<br/>
|
||||
`}`<br/>
|
||||
`if (nukeCount == k_maxNuke)`<br/>
|
||||
`{`<br/>
|
||||
`break;`<br/>
|
||||
`}`<br/>
|
||||
`}`<br/>
|
||||
`}`<br/>
|
||||
`// Sort the nuke array to group duplicates.`<br/>
|
||||
`std::sort(nuke, nuke + nukeCount);`<br/>
|
||||
`// Destroy the bodies, skipping duplicates.`<br/>
|
||||
`int32 i = 0;`<br/>
|
||||
`while (i < nukeCount)`<br/>
|
||||
`{`<br/>
|
||||
`b2Body* b = nuke[i++];`<br/>
|
||||
`while (i < nukeCount && nuke[i] ==
|
||||
b)`<br/>
|
||||
`{`<br/>
|
||||
`++i;`<br/>
|
||||
`}`<br/>
|
||||
`m_world->DestroyBody(b);`<br/>
|
||||
`}`<br/>
|
||||
|
||||
## Contact Filtering
|
||||
|
||||
Often in a game you don't want all objects to collide. For example, you may
|
||||
want to create a door that only certain characters can pass through. This is
|
||||
called contact filtering, because some interactions are filtered out.
|
||||
|
||||
LiquidFun allows you to achieve custom contact filtering by implementing a
|
||||
b2ContactFilter class. This class requires you to implement a ShouldCollide
|
||||
function that receives two b2Shape pointers. Your function returns true if the
|
||||
shapes should collide.
|
||||
|
||||
The default implementation of ShouldCollide uses the b2FilterData defined in
|
||||
Chapter 6, Fixtures.
|
||||
|
||||
`bool b2ContactFilter::ShouldCollide(b2Fixture* fixtureA,
|
||||
b2Fixture* fixtureB)`<br/>
|
||||
`{`<br/>
|
||||
`const b2Filter& filterA =
|
||||
fixtureA->GetFilterData();`<br/>
|
||||
`const b2Filter& filterB =
|
||||
fixtureB->GetFilterData();`<br/>
|
||||
`if (filterA.groupIndex ==
|
||||
filterB.groupIndex && filterA.groupIndex != 0)`<br/>
|
||||
`{`<br/>
|
||||
`return
|
||||
filterA.groupIndex > 0;`<br/>
|
||||
`}`<br/>
|
||||
`bool collide = (filterA.maskBits & filterB.categoryBits) != 0 && (filterA.categoryBits & filterB.maskBits) != 0;`<br/>
|
||||
`return collide;`<br/>
|
||||
`}`<br/>
|
||||
|
||||
At run-time you can create an instance of your contact filter and register it
|
||||
with b2World::SetContactFilter. Make sure your filter stays in scope while the
|
||||
world exists.
|
||||
|
||||
`MyContactFilter filter;`<br/>
|
||||
`world->SetContactFilter(&filter);`<br/>
|
||||
`// filter remains in scope …`<br/>
|
||||
|
||||
|
||||
*This content is licensed under
|
||||
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
|
||||
For details and restrictions, please see the
|
||||
[Content License](md__content_license.html).*
|
||||
@@ -0,0 +1,322 @@
|
||||
# World Class
|
||||
|
||||
[About](#about)<br/>
|
||||
[Creating and Destroying a World](#cdw)<br/>
|
||||
[Using a World](#uw)<br/>
|
||||
[Simulation](#sim)<br/>
|
||||
[Exploring the World](#ew)<br/>
|
||||
[AABB Queries](#ab)<br/>
|
||||
[Ray Casts](#rc)<br/>
|
||||
[Forces and Impulses](#fi)<br/>
|
||||
[Coordinate Transformations](#ct)<br/>
|
||||
[Lists](#lists)<br/>
|
||||
<a name="about"></a><br/>
|
||||
## About
|
||||
|
||||
The b2World class contains the bodies and joints. It manages all aspects of
|
||||
the simulation and allows for asynchronous queries (like AABB queries and
|
||||
ray-casts). Much of your interactions with LiquidFun will be with a b2World
|
||||
object.
|
||||
|
||||
<a name="cdw"></a><br/>
|
||||
## Creating and Destroying a World
|
||||
|
||||
Creating a world is fairly simple. You just need to provide a gravity vector
|
||||
and a Boolean indicating if bodies can sleep. Usually you will create and
|
||||
destroy a world using new and delete.
|
||||
|
||||
`b2World* myWorld = new b2World(gravity, doSleep);`<br/>
|
||||
`... do stuff ...`<br/>
|
||||
`delete myWorld;`<br/>
|
||||
|
||||
<a name="uw"></a><br/>
|
||||
## Using a World
|
||||
|
||||
The world class contains factories for creating and destroying bodies and
|
||||
joints. These factories are discussed later in the sections on bodies and
|
||||
joints. There are some other interactions with b2World that I will cover now.
|
||||
|
||||
<a name="sim"></a><br/>
|
||||
## Simulation
|
||||
|
||||
The world class is used to drive the simulation. You specify a time step and a
|
||||
velocity and position iteration count. For example:
|
||||
|
||||
`float32 timeStep = 1.0f / 60.f;`<br/>
|
||||
`int32 velocityIterations = 10;`<br/>
|
||||
`int32 positionIterations = 8;`<br/>
|
||||
`myWorld->Step(timeStep, velocityIterations,
|
||||
positionIterations);`<br/>
|
||||
|
||||
After the time step you can examine your bodies and joints for information.
|
||||
Most likely you will grab the position off the bodies so that you can update
|
||||
your actors and render them. You can perform the time step anywhere in your
|
||||
game loop, but you should be aware of the order of things. For example, you
|
||||
must create bodies before the time step if you want to get collision results
|
||||
for the new bodies in that frame.
|
||||
|
||||
As I discussed above in the HelloWorld tutorial, you should use a fixed time
|
||||
step. By using a larger time step you can improve performance in low frame
|
||||
rate scenarios. But generally you should use a time step no larger than 1/30
|
||||
seconds. A time step of 1/60 seconds will usually deliver a high quality
|
||||
simulation.
|
||||
|
||||
The iteration count controls how many times the constraint solver sweeps over
|
||||
all the contacts and joints in the world. More iteration always yields a
|
||||
better simulation. But don't trade a small time step for a large iteration
|
||||
count. 60Hz and 10 iterations is far better than 30Hz and 20 iterations.
|
||||
|
||||
After stepping, you should clear any forces you have applied to your bodies.
|
||||
This is done with the command b2World::ClearForces. This lets you take
|
||||
multiple sub-steps with the same force field.
|
||||
|
||||
`myWorld->ClearForces();`<br/>
|
||||
|
||||
|
||||
<a name="ew"></a><br/>
|
||||
## Exploring the World
|
||||
|
||||
The world is a container for bodies, contacts, and joints. You can grab the
|
||||
body, contact, and joint lists off the world and iterate over them. For
|
||||
example, this code wakes up all the bodies in the world:
|
||||
|
||||
`for (b2Body* b = myWorld->GetBodyList(); b; b =
|
||||
b->GetNext())`<br/>
|
||||
`{`<br/>
|
||||
` b->SetAwake(true);`<br/>
|
||||
`}`<br/>
|
||||
|
||||
Unfortunately real programs can be more complicated. For example, the
|
||||
following code is broken:
|
||||
|
||||
`for (b2Body* b = myWorld->GetBodyList(); b; b =
|
||||
b->GetNext())`<br/>
|
||||
`{`<br/>
|
||||
`GameActor* myActor =
|
||||
(GameActor*)b->GetUserData();`<br/>
|
||||
`if (myActor->IsDead())`<br/>
|
||||
`{`<br/>
|
||||
`myWorld->DestroyBody(b);
|
||||
// ERROR: now GetNext returns garbage.`<br/>
|
||||
`}`<br/>
|
||||
`}`<br/>
|
||||
|
||||
Everything goes ok until a body is destroyed. Once a body is destroyed, its
|
||||
next pointer becomes invalid. So the call to b2Body::GetNext() will return
|
||||
garbage. The solution to this is to copy the next pointer before destroying
|
||||
the body.
|
||||
|
||||
`b2Body* node = myWorld->GetBodyList();`<br/>
|
||||
`while (node)`<br/>
|
||||
`{`<br/>
|
||||
`b2Body* b = node;`<br/>
|
||||
`node = node->GetNext();`<br/>
|
||||
`GameActor* myActor =
|
||||
(GameActor*)b->GetUserData();`<br/>
|
||||
`if (myActor->IsDead())`<br/>
|
||||
`{`<br/>
|
||||
`myWorld->DestroyBody(b);`
|
||||
<br/>
|
||||
`}`<br/>
|
||||
`}`<br/>
|
||||
|
||||
This safely destroys the current body. However, you may want to call a game
|
||||
function that may destroy multiple bodies. In this case you need to be very
|
||||
careful. The solution is application specific, but for convenience I'll show
|
||||
one method of solving the problem.
|
||||
|
||||
`b2Body* node = myWorld->GetBodyList();`<br/>
|
||||
`while (node)`<br/>
|
||||
`{`<br/>
|
||||
`b2Body* b = node;`<br/>
|
||||
`node = node->GetNext();`<br/>
|
||||
`GameActor* myActor =
|
||||
(GameActor*)b->GetUserData();`<br/>
|
||||
`if (myActor->IsDead())`<br/>
|
||||
`{`<br/>
|
||||
`bool
|
||||
otherBodiesDestroyed = GameCrazyBodyDestroyer(b);`<br/>
|
||||
`if
|
||||
(otherBodiesDestroyed)`<br/>
|
||||
`{`<br/>
|
||||
`node
|
||||
= myWorld->GetBodyList();`<br/>
|
||||
`}`<br/>
|
||||
`}`<br/>
|
||||
`}`<br/>
|
||||
|
||||
Obviously to make this work, GameCrazyBodyDestroyer must be honest about what
|
||||
it has destroyed.
|
||||
|
||||
<a name="ab"></a><br/>
|
||||
## AABB Queries
|
||||
|
||||
Sometimes you want to determine all the shapes in a region. The b2World class
|
||||
has a fast log(N) method for this using the broad-phase data structure. You
|
||||
provide an AABB in world coordinates and an implementation of b2QueryCallback.
|
||||
The world calls your class with each fixture whose AABB overlaps the query
|
||||
AABB. Return true to continue the query, otherwise return false. For example,
|
||||
the following code finds all the fixtures that potentially intersect a
|
||||
specified AABB and wakes up all of the associated bodies.
|
||||
|
||||
`class MyQueryCallback : public b2QueryCallback`<br/>
|
||||
`{`<br/>
|
||||
`public:`<br/>
|
||||
`bool ReportFixture(b2Fixture*
|
||||
fixture)`<br/>
|
||||
`{`<br/>
|
||||
`b2Body* body =
|
||||
fixture->GetBody();`<br/>
|
||||
`body->SetAwake(true);`<br
|
||||
/>
|
||||
`// Return true to
|
||||
continue the query.`<br/>
|
||||
`return true;`<br/>
|
||||
`}`<br/>
|
||||
`};`<br/>
|
||||
`...`<br/>
|
||||
`MyQueryCallback callback;`<br/>
|
||||
`b2AABB aabb;`<br/>
|
||||
`aabb.lowerBound.Set(-1.0f, -1.0f);`<br/>
|
||||
`aabb.upperBound.Set(1.0f, 1.0f);`<br/>
|
||||
`myWorld->Query(&callback, aabb);`<br/>
|
||||
|
||||
You cannot make any assumptions about the order of the callbacks.
|
||||
|
||||
<a name="rc"></a><br/>
|
||||
## Ray Casts
|
||||
|
||||
You can use ray casts to do line-of-sight checks, fire guns, etc. You perform
|
||||
a ray cast by implementing a callback class and providing the start and end
|
||||
points. The world class calls your class with each fixture hit by the ray.
|
||||
Your callback is provided with the fixture, the point of intersection, the
|
||||
unit normal vector, and the fractional distance along the ray. You cannot make
|
||||
any assumptions about the order of the callbacks.
|
||||
|
||||
You control the continuation of the ray cast by returning a fraction.
|
||||
Returning a fraction of zero indicates the ray cast should be terminated. A
|
||||
fraction of one indicates the ray cast should continue as if no hit occurred.
|
||||
If you return the fraction from the argument list, the ray will be clipped to
|
||||
the current intersection point. So you can ray cast any shape, ray cast all
|
||||
shapes, or ray cast the closest shape by returning the appropriate fraction.
|
||||
|
||||
You may also return of fraction of -1 to filter the fixture. Then the ray cast
|
||||
will proceed as if the fixture does not exist.
|
||||
|
||||
Here is an example:
|
||||
|
||||
`// This class captures the closest hit shape.`<br/>
|
||||
`class MyRayCastCallback : public b2RayCastCallback`<br/>
|
||||
`{`<br/>
|
||||
`public:`<br/>
|
||||
`MyRayCastCallback()`<br/>
|
||||
`{`<br/>
|
||||
`m_fixture = NULL;`<br/>
|
||||
`}`<br/>
|
||||
`float32 ReportFixture(b2Fixture* fixture,
|
||||
const b2Vec2& point, const b2Vec2& normal, float32 fraction)`<br/>
|
||||
`{`<br/>
|
||||
`m_fixture =
|
||||
fixture;`<br/>
|
||||
`m_point = point;`<br/>
|
||||
`m_normal = normal;`<br/>
|
||||
`m_fraction =
|
||||
fraction;`<br/>
|
||||
`return fraction;`<br/>
|
||||
`}`<br/>
|
||||
`b2Fixture* m_fixture;`<br/>
|
||||
`b2Vec2 m_point;`<br/>
|
||||
`b2Vec2 m_normal;`<br/>
|
||||
`float32 m_fraction;`<br/>
|
||||
`};`<br/>
|
||||
`MyRayCastCallback callback;`<br/>
|
||||
`b2Vec2 point1(-1.0f, 0.0f);`<br/>
|
||||
`b2Vec2 point2(3.0f, 1.0f);`<br/>
|
||||
`myWorld->RayCast(&callback, point1, point2);`<br/>
|
||||
|
||||
Caution
|
||||
|
||||
Due to round-off errors, ray casts can sneak through small cracks
|
||||
between polygons in your static environment. If this is not acceptable in your
|
||||
application, please enlarge your polygons slightly.
|
||||
|
||||
`void SetLinearVelocity(const b2Vec2& v);`<br/>
|
||||
`b2Vec2 GetLinearVelocity() const;`<br/>
|
||||
`void SetAngularVelocity(float32 omega);`<br/>
|
||||
`float32 GetAngularVelocity() const;`<br/>
|
||||
|
||||
<a name="fi"></a><br/>
|
||||
## Forces and Impulses
|
||||
|
||||
You can apply forces, torques, and impulses to a body. When you apply a force
|
||||
or an impulse, you provide a world point where the load is applied. This often
|
||||
results in a torque about the center of mass.
|
||||
|
||||
void ApplyForce(const b2Vec2& force, const b2Vec2& point);
|
||||
void ApplyTorque(float32 torque);
|
||||
void ApplyLinearImpulse(const b2Vec2& impulse, const b2Vec2& point);
|
||||
void ApplyAngularImpulse(float32 impulse);
|
||||
|
||||
Applying a force, torque, or impulse wakes the body. Sometimes this is
|
||||
undesirable. For example, you may be applying a steady force and want to allow
|
||||
the body to sleep to improve performance. In this case you can use the
|
||||
following code.
|
||||
|
||||
`if (myBody->IsAwake() == true)`<br/>
|
||||
`{`<br/>
|
||||
`myBody->ApplyForce(myForce,
|
||||
myPoint);`<br/>
|
||||
`}`<br/>
|
||||
|
||||
You can apply forces and impulses to particles and particle groups, as well.
|
||||
Unlike with bodies, however, the load is not applied to an arbitrary world
|
||||
point. Instead, it acts upon the center of each particle.
|
||||
|
||||
You can apply forces and impulses to individual particles or to particle groups.
|
||||
The following example spreads an impulse of (0.7, 0.3) kg m/s across the
|
||||
particles in myParticleGroup:
|
||||
|
||||
`const b2Vec2 impulse(0.7f, 0.3f);`
|
||||
`myParticleGroup->ApplyLinearImpulse(impulse);`
|
||||
|
||||
<a name="ct"></a><br/>
|
||||
## Coordinate Transformations
|
||||
|
||||
The body class has some utility functions to help you transform points and
|
||||
vectors between local and world space. If you don't understand these concepts,
|
||||
please read "Essential Mathematics for Games and Interactive Applications" by
|
||||
Jim Van Verth and Lars Bishop. These functions are efficient (when inlined).
|
||||
|
||||
`b2Vec2 GetWorldPoint(const b2Vec2& localPoint);`<br/>
|
||||
|
||||
`b2Vec2 GetWorldVector(const b2Vec2& localVector);`<br/>
|
||||
|
||||
`b2Vec2 GetLocalPoint(const b2Vec2& worldPoint);`<br/>
|
||||
|
||||
`b2Vec2 GetLocalVector(const b2Vec2& worldVector);`<br/>
|
||||
|
||||
<a name="lists"></a><br/>
|
||||
## Lists
|
||||
|
||||
You can iterate over a body's fixtures. This is mainly useful if you need to
|
||||
access the fixture's user data.
|
||||
|
||||
`for (b2Fixture* f = body->GetFixtureList(); f; f =
|
||||
f->GetNext())`<br/>
|
||||
`{`<br/>
|
||||
`MyFixtureData* data =
|
||||
(MyFixtureData*)f->GetUserData();`<br/>
|
||||
`… do something with data …`<br/>
|
||||
`}`<br/>
|
||||
|
||||
You can similarly iterate over the body's joint list.
|
||||
|
||||
The body also provides a list of associated contacts. You can use this to get
|
||||
information about the current contacts. Be careful, because the contact list
|
||||
may not contain all the contacts that existed during the previous time step.
|
||||
|
||||
|
||||
*This content is licensed under
|
||||
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
|
||||
For details and restrictions, please see the
|
||||
[Content License](md__content_license.html).*
|
||||
@@ -0,0 +1,729 @@
|
||||
# Particle Module
|
||||
|
||||
[About](#About)<br/>
|
||||
[Particles](#Particles)<br/>
|
||||
[Particle Systems](#ps)<br/>
|
||||
[Particle Groups](#pg)<br/>
|
||||
[Discrete Particles vs. Particle Groups](#dp)<br/>
|
||||
[Stepping the World](#stw)<br/>
|
||||
[Maximum Velocity](#mv)<br/>
|
||||
[Creating and Destroying Particles](#cdp)<br/>
|
||||
[Creating and Destroying Particle Groups](#cdpg)<br/>
|
||||
[Particle Behaviors](#pb)<br/>
|
||||
[Particle Properties](#pp)<br/>
|
||||
[Rendering with OpenGL](#gl)<br/>
|
||||
[Sample Applications](#sa)<br/>
|
||||
|
||||
<a name="About">
|
||||
## About
|
||||
The Particle module offers the ability to create and manipulate liquid or soft
|
||||
(deformable) bodies. It allows you to create (and destroy) particles with
|
||||
various behaviors and properties, and provides various methods for
|
||||
manipulating
|
||||
them. The module permits you to define particles discretely or as groups. It
|
||||
is
|
||||
designed to allow you to manipulate large numbers of particles efficiently.
|
||||
|
||||
<a name="Particles">
|
||||
## Particles
|
||||
A particle is round, and the minimal unit of matter in a particle system. By
|
||||
default, a particle behaves as a liquid. You can set behavioral flags,
|
||||
however, to assign different behaviors (explained in
|
||||
[Particle Behaviors](#pb)) to individual particles or groups of particles.
|
||||
You can also set other particle properties including position, velocity, and
|
||||
color.<br/>
|
||||
The `b2Particle.h` file contains the enumerated behavior values, as well as
|
||||
the
|
||||
variables specifying other particle properties. The corresponding enum is
|
||||
named `b2ParticleFlag.`
|
||||
|
||||
<a name="ps">
|
||||
## Particle Systems
|
||||
|
||||
The "world" that particles inhabit is called a particle system. A particle
|
||||
system describes a wide variety of physical coefficients that help dictate
|
||||
how particles interact with the world around them. A few examples of these
|
||||
conditions are default particle radius, elasticity, and viscosity. For more
|
||||
detail, see the API Reference description of the b2ParticleSystemDef struct.
|
||||
|
||||
The following example creates a particle system:
|
||||
|
||||
`const b2ParticleSystemDef particleSystemDef;`<br/>
|
||||
`m_particleSystems[0] =
|
||||
`m_world->CreateParticleSystem(&particleSystemDef);`<br/>
|
||||
|
||||
You can also create more than one particle system: Thus, one "world's"
|
||||
particles may have a certain default radius, elasticity, etc., while the other
|
||||
"world" has different default values for these properties. The following sample
|
||||
shows the creation of multiple particle systems:
|
||||
|
||||
`const b2ParticleSystemDef particleSystemDef;`<br/>
|
||||
`for (int i = 0; i < NUM_PARTICLE_SYSTEMS; ++i) {`<br/>
|
||||
|
||||
`m_particleSystems[i] = m_world->CreateParticleSystem(&particleSystemDef);`
|
||||
<br/>
|
||||
`}`<br/>
|
||||
|
||||
In many, if not most,
|
||||
cases, it will not be necessary to adjust the default values or create multiple
|
||||
particle systems. You may find it useful in some cases, however.
|
||||
|
||||
For example, dividing particles into multiple systems can yield a performance
|
||||
gain by allowing you to simulate only the visible systems while putting all
|
||||
other systems in a "paused" state using `b2ParticleSystem::SetPaused()`.
|
||||
|
||||
The "Multiple Systems" example in the Testbed provides an example of
|
||||
two particle systems influencing a rigid body while not interacting with each
|
||||
other.
|
||||
|
||||
<a name="pg">
|
||||
## Particle Groups
|
||||
|
||||
Instead of creating particles individually, you can create a group of
|
||||
particles to manipulate en masse. Some of the particle-group properties that
|
||||
you can set are the same as those for discrete particles: behavior, position,
|
||||
linear velocity, and color. There are also properties specific to groups:
|
||||
rotational angle, rotational velocity, and strength.<br/>
|
||||
The `b2ParticleGroup.h` file contains the declarations for all of these
|
||||
variables, as well as the enum for particle-group behavior:
|
||||
`b2ParticleGroupFlag`.
|
||||
|
||||
<a name="dp">
|
||||
## Discrete Particles vs. Particle Groups
|
||||
|
||||
With one main exception, there is no functional difference between working
|
||||
with individual particles and groups of particles. The exception is rigid
|
||||
particles: Because of the internal algorithm used to make particles rigid, you
|
||||
must define them as a group.
|
||||
|
||||
Particle groups do offer several conveniences. First, they allow you to create
|
||||
and destroy large numbers of particles automatically. If you do not create a
|
||||
group, you must create all of the particles individually. Also, a group allows
|
||||
you to assign the same property, such as angle of rotation, to all of its
|
||||
particles at once.
|
||||
|
||||
<a name="stw">
|
||||
## Stepping the World (Particle Iterations)
|
||||
|
||||
The particle solver can iterate multiple times per step. Larger numbers of
|
||||
steps improve the stability and fidelity of the particle simulation. However,
|
||||
more steps also require more processor cycles.
|
||||
|
||||
The cycles cost is almost linear: double the number of particle iterations
|
||||
will almost double the cycles cost of b2ParticleSystem::Solve.
|
||||
|
||||
Use the `particleIterations` parameter in `b2World::Step` to set the number
|
||||
of iterations. The default value of `particleIterations` is 1.
|
||||
|
||||
You should experiment with `particleIterations` in your game to find the best
|
||||
balance of stability versus cycles. Try calling `b2CalculateParticleIterations`
|
||||
or `b2World::CalculateReasonableParticleIterations` to estimate a reasonable
|
||||
value. Note that these functions are, necessarily, a simplification, and
|
||||
should be used only as a starting point.
|
||||
|
||||
If your simulation seems overly bouncy or energetic, or if the particles in
|
||||
your simulation are passing through contacts, try increasing the number of
|
||||
particle iterations.
|
||||
|
||||
Note that, as particle iterations increases, the affect of pressure on
|
||||
highly-compressed particles also increases. That is, particles get more
|
||||
incompressible as you increase particle iterations.
|
||||
|
||||
<a name="mv">
|
||||
## Maximum Velocity
|
||||
|
||||
The particle simulation enforces a maximum velocity on the particles, for
|
||||
stability and to prevent excessive interpenetration. The maximum velocity is,
|
||||
|
||||
`particle diameter / (particle iterations *
|
||||
b2World::Step's dt)`<br/>
|
||||
|
||||
<a name="cdp">
|
||||
## Creating and Destroying Particles
|
||||
|
||||
To create individual particles, create a `b2ParticleDef`-struct object. Next,
|
||||
specify the behavior and properties of the particle. Finally, call the method
|
||||
to create the particle.<br/>
|
||||
The following example creates an individual particle.
|
||||
|
||||
`b2ParticleDef pd;`<br>
|
||||
`pd.flags = b2_elasticParticle;`<br/>
|
||||
`pd.color.Set(0, 0, 255, 255);`<br/>
|
||||
`pd.position.Set(i, 0);`<br/>
|
||||
`int tempIndex = m_particleSystem->CreateParticle(pd);`<br/>
|
||||
|
||||
Particle lists are self-compacting. Therefore, the index returned by
|
||||
CreateParticle is only valid until a lower-indexed particle, or a group
|
||||
referencing a lower-indexed particle, is deleted.<br/>
|
||||
To destroy an individual particle, invoke the function
|
||||
|
||||
`void DestroyParticle(int32 index);`
|
||||
|
||||
The following example destroys the particle created above.
|
||||
|
||||
`m_particleSystem->DestroyParticle(tempIndex);`<br/>
|
||||
|
||||
### Particle lifetimes
|
||||
|
||||
In addition to manual destruction of particles as described above, particles
|
||||
can also expire and be destroyed due to age.
|
||||
|
||||
The following example tells the system to track particle ages for the purpose
|
||||
of destroying them.
|
||||
|
||||
`m_particleSystem->SetParticleDestructionByAge(true);`
|
||||
|
||||
A particle can die one of two "age-related" deaths. First, you can set a
|
||||
lifetime for a particle--a period of time after which it expires. The following
|
||||
example does this:
|
||||
|
||||
`m_particleSystem->SetParticleLifetime(`<br/>
|
||||
`index, Random() *`<br/>
|
||||
|
||||
`(k_particleLifetimeMax - k_particleLifetimeMin) +`<br/>
|
||||
`k_particleLifetimeMin);`<br/>
|
||||
|
||||
where `index` specifies the number of the particle whose lifetime is being
|
||||
assigned, and the `Random()` function generates a random value for that
|
||||
lifetime.
|
||||
|
||||
You do not need to set a specific lifetime for a particle for it to have an
|
||||
age-related death. If you set a maximum number of particles that can exist in a
|
||||
particle system, and you have have told the system to track particle ages, the
|
||||
system clamps particle count by culling "excess" particles. Particle culling
|
||||
takes place in age order, with the oldest ones destroyed first.
|
||||
|
||||
The following example sets a maximum particle count for a particle system.
|
||||
|
||||
`m_particleSystem->SetMaxParticleCount(k_maxParticleCount);`
|
||||
|
||||
The Faucet example in the Testbed provides an example of both types of
|
||||
lifetime-driven particle destruction.
|
||||
|
||||
### Stuck Particles
|
||||
|
||||
Particles may get stuck and become obstructions that need to be destroyed or
|
||||
relocated. A particle is identified as possibly stuck if it remains in contact
|
||||
with two or more surfaces for a user-specified number (threshold) of particle
|
||||
iterations. Once "candidates" are identified, you can implement your own logic
|
||||
to decide whether they are actually stuck, and how to deal with them.
|
||||
|
||||
The ability to implement your own logic gives you flexibility in deciding
|
||||
when you want to consider a particle stuck. For instance, a ball may
|
||||
be traveling down a chute, making contact with walls on multiple sides. This
|
||||
state satisfies the "possibly stuck" condition described in the previous
|
||||
paragraph. But you could implement logic judging the ball not stuck as long
|
||||
as it keeps traveling down the chute.
|
||||
|
||||
On the other hand, you could also decide that not only an immobile particle,
|
||||
but even a mobile one trapped in a certain spatial range, is stuck. The system
|
||||
relies on you to judge the candidates.
|
||||
|
||||
The following example shows one possible implementation for such a case.
|
||||
|
||||
<pre>
|
||||
// This code example of app logic deciding whether or not to eliminate stuck
|
||||
// particles shows a user who set up a global array of sensor fixtures
|
||||
// covering areas they know to be "problematic" for stuck particles in
|
||||
// their geometry, and then at each step testing any stuck particles against
|
||||
// those sensors, eliminating any stuck particles that lie inside a known
|
||||
// problem region.
|
||||
void DestroyStuckParticlesInSensors(
|
||||
const b2Fixture * const *sensors, int32 num)
|
||||
{
|
||||
const int32 stuck = gParticleSystem->GetStuckCandidateCount();
|
||||
if (stuck > 0)
|
||||
{
|
||||
const int32 *candidates = gParticleSystem->GetStuckCandidates();
|
||||
const b2Vec2 *positions = gParticleSystem->GetPositionBuffer();
|
||||
for (int32 i = 0; i < stuck; ++i)
|
||||
{
|
||||
const int32 particle = candidates[i];
|
||||
const b2Vec2 &position = positions[particle];
|
||||
for (int32 j = 0; j < num; ++j)
|
||||
{
|
||||
if(sensors[j]->TestPoint(position))
|
||||
{
|
||||
gParticleSystem->DestroyParticle(particle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// particles in multiple contacts for 5 or more iterations are
|
||||
// candidates
|
||||
gParticleSystem->SetStuckThreshold(5);
|
||||
|
||||
// step the world (assuming the timestep, velocity iterations,
|
||||
// and position iterations have been set globally).
|
||||
gWorld->Step(gTimeStep, gVelocityIterations, gPositionIterations);
|
||||
|
||||
// Perform the above check for stuck particles against sensors
|
||||
// in this global array.
|
||||
DestroyStuckParticlesInSensors(gProblemAreaSensors, gNumSensors);
|
||||
</pre>
|
||||
|
||||
<a name="cdpg">
|
||||
## Creating and Destroying Particle Groups
|
||||
|
||||
A particle group begins life in a shaped container. You must therefore start a
|
||||
particle group definition by specifying a shape. Next, create a
|
||||
b2ParticleGroupDef-struct object. Then, specify the behavior and properties of
|
||||
the particles themselves. Finally, call the method to create a particle
|
||||
group.<br/>
|
||||
The following example creates five differently colored, box-shaped groups of
|
||||
particles.
|
||||
|
||||
`b2ParticleGroupDef pd;`<br/>
|
||||
`b2PolygonShape shape;`<br/>
|
||||
`shape.SetAsBox(10, 5);`<br/>
|
||||
`pd.shape = &shape;`<br/>
|
||||
`pd.flags = b2_elasticParticle;`<br/>
|
||||
`pd.angle = -0.5f;`<br/>
|
||||
`pd.angularVelocity = 2.0f;`<br/>
|
||||
`for (int32 i = 0; i < 5; i++)`<br/>
|
||||
`{`<br/>
|
||||
`pd.position.Set(10 + 20 * i, 40);`<br/>
|
||||
`pd.color.Set(i * 255 / 5, 255 - i * 255 /
|
||||
5, 128, 255);`<br/>
|
||||
|
||||
`m_particleSystem->CreateParticleGroup(pd);`<br/>
|
||||
`}`<br/>
|
||||
|
||||
To destroy a particles in a group, invoke the function
|
||||
|
||||
`DestroyParticles(bool callDestructionListener);`<br/>
|
||||
|
||||
Groups are automatically destroyed when they contain no particles if the
|
||||
`b2_particleGroupCanBeEmpty` is not set in the group's flags.
|
||||
|
||||
The following example destroys all particle groups in the particle system.
|
||||
|
||||
<pre>
|
||||
b2ParticleGroup* group = m_particleSystem->GetParticleGroupList();
|
||||
while (group)
|
||||
{
|
||||
m_particleSystem->SetGroupFlags(
|
||||
m_particleSystem->GetGroupFlags() & ~b2_particleGroupCanBeEmpty);
|
||||
group->DestroyParticles(false);
|
||||
// The destruction of particle groups are deferred to the next call of
|
||||
// Step() so it's safe to reference the group here.
|
||||
group = group->GetNext();
|
||||
}
|
||||
</pre>
|
||||
|
||||
The next several sections provide more information on how to define particle
|
||||
behaviors and properties.
|
||||
|
||||
<a name="pb">
|
||||
## Particle Behaviors
|
||||
|
||||
Particle behaviors are defined either for entire groups of, or individual,
|
||||
particles.
|
||||
|
||||
For a group of particles, use the `b2ParticleGroupFlag` enum, which provides
|
||||
two types of particle groups:
|
||||
|
||||
###Solid
|
||||
|
||||
A solid particle group prevents other bodies from lodging inside of it. Should
|
||||
anything penetrate it, the solid particle group pushes the offending body back
|
||||
out to its surface.
|
||||
|
||||
A solid particle group also possesses an especially strong repulsive force. It
|
||||
is useful, for example, in a case where:
|
||||
|
||||
* Something should be expected to bounce with unusual vigor
|
||||
** As when a racquetball strikes the wall of a court
|
||||
|
||||
Use the `b2_SolidParticleGroup` flag of the `b2ParticleGroupFlag` enum to
|
||||
specify a solid particle group. For example:
|
||||
|
||||
`pd.groupFlags = b2_solidParticleGroup;`
|
||||
|
||||
###Rigid
|
||||
|
||||
Rigid particle groups are ones whose shape does not change, even when they
|
||||
collide
|
||||
with other bodies. Working with rigid particle groups confers a few advantages
|
||||
over simply
|
||||
working with rigid bodies: With a rigid particle group, you can:<br/>
|
||||
|
||||
* Delete part of the group (i.e., some of its particles).
|
||||
* For example, firing a bullet that leaves a hole in a box-shaped group of
|
||||
particles.
|
||||
* Merge it with other groups.
|
||||
* For example, creating a snowman from three round particle groups, and
|
||||
then merging them into a single particle group.
|
||||
|
||||
Use the `b2_rigidParticleGroup` flag of the `b2ParticleGroupFlag` enum to
|
||||
specify a rigid particle group. For example:
|
||||
|
||||
`pd.groupFlags = b2_rigidParticleGroup;`
|
||||
|
||||
For individual particles, use the b2ParticleFlag enum. The b2ParticleFlag enum
|
||||
provides the flags described in the following sections. Note that different
|
||||
particle behaviors may exact different performance costs.
|
||||
|
||||
### Elastic
|
||||
|
||||
Elastic particles deform and may also bounce when they collide with rigid
|
||||
bodies.<br/>
|
||||
Set particle behavior as elastic using the statement
|
||||
|
||||
`pd.flags = b2_elasticParticle;`
|
||||
|
||||
The green circle and the blue box in the "Elastic Particles" demo of the
|
||||
Testbed application comprise elastic particles.
|
||||
|
||||
### Color-mixing
|
||||
|
||||
Color-mixing particles take on some of the color of other particles with which
|
||||
they collide. If only one of the two colliding particles is a color-mixing
|
||||
one,
|
||||
the other particle retains its pre-collision color.<br/>
|
||||
<br/>
|
||||
The following example shows how color mixture is calculated. It shows the
|
||||
collision of two color-mixing particles: one red ("R") and one green ("G").
|
||||
|
||||
1. First, the system calculates deltaColor, which is the value by which each
|
||||
color will change.
|
||||
|
||||
deltaColor = colorMixingStrength * (B's color - A's color).<br/>
|
||||
= 0.5 * ((0,255,0,255) - (255,0,0,255))<br/>
|
||||
= 0.5 * (-255,255,0,0)<br/>
|
||||
= (-127.5,127.5,0,0)
|
||||
|
||||
2. Then, it applies the delta to each particle
|
||||
|
||||
R's color += deltaColor<br/>
|
||||
G's color -= deltaColor
|
||||
|
||||
3. As a result, both particles are now yellow:
|
||||
|
||||
A's color = (127.5,127.5,0,255)<br/>
|
||||
B's color = (127.5,127.5,0,255)<br/>
|
||||
<br/>
|
||||
Note that when one of the operations in step 2 results in a negative number,
|
||||
the
|
||||
system uses the absolute value of that number. When it results in a value over
|
||||
255, it rolls over from zero.<br/>
|
||||
Set particle behavior as color-mixing using the statement<br/>
|
||||
`pd.flags = b2_colorMixingParticle;`
|
||||
|
||||
The "Surface Tension" demo of the Testbed application uses color-mixing
|
||||
particles.
|
||||
|
||||
### Powder
|
||||
|
||||
Powder particles produce a scattering effect such as you might see with sand
|
||||
or
|
||||
dust.<br/>
|
||||
Set particle behavior as powder using the statement<br/>
|
||||
|
||||
`pd.flags = b2_powderParticle;`
|
||||
|
||||
The "Sparky" demo of the Testbed application uses powder particles.
|
||||
|
||||
### Spring
|
||||
|
||||
Spring particles produce the effect of being attached to one another, as by a
|
||||
spring. Particles are "connected" in pairs. Each particle is connected to the
|
||||
one that was closest to it at time of creation. Once paired, particles do not
|
||||
change "partners." The farther an external force pulls them from one another,
|
||||
the greater the power with which they collide when that external force is
|
||||
removed. No matter how far particles get from one another, the connection
|
||||
between them does not "snap."<br/>
|
||||
Set spring behavior using the statement<br/>
|
||||
|
||||
`pd.flags = b2_springParticle;`
|
||||
|
||||
The red circle in the "Elastic Particles" demo of the Testbed application
|
||||
comprises spring particles.
|
||||
|
||||
### Tensile
|
||||
|
||||
Tensile particles are used to produce the effect of surface tension, or the
|
||||
taut
|
||||
curvature on the surface of a body of liquid. They might be used, for example,
|
||||
to create the surface tension you would see on a drop of water.<br/>
|
||||
Once the tension is broken, the particles bounce as if they were elastic, but
|
||||
also continue to attract each other. As a result, particles tend to form
|
||||
clusters as they bounce.<br/>
|
||||
Set tensile behavior using the statement
|
||||
|
||||
`pd.flags = b2_tensileParticle;`
|
||||
|
||||
The "Surface Tension" demo of the Testbed application uses tensile particles.
|
||||
|
||||
### Viscous
|
||||
|
||||
Viscous particles exhibit clinginess or stickiness, like oil.<br/>
|
||||
Set viscous behavior using the statement
|
||||
|
||||
`pd.flags = b2_viscousParticle;`
|
||||
|
||||
The "Liquid Timer" demo of the Testbed application uses viscous particles.
|
||||
|
||||
### Static Pressure
|
||||
|
||||
Particles are subject to compression when pressure acts upon them. For example,
|
||||
when particles pour into a container, the ones at the bottom of the container
|
||||
are "crushed" under the weight of those above them and packed more tightly
|
||||
together than the ones at the top of the pile.
|
||||
|
||||
The static-pressure particle eliminates this differential; the same amount of
|
||||
pressure acts upon each particle in the group.
|
||||
|
||||
The following example sets static-pressure behavior.
|
||||
|
||||
`pd.flags = b2_staticPressureParticle;`
|
||||
|
||||
### Wall
|
||||
|
||||
Wall particles are static. They are permanently stationary, even if something
|
||||
collides with them. <br/>
|
||||
Set wall behavior using the statement
|
||||
|
||||
`pd.flags = b2_wallParticle;`
|
||||
|
||||
### Barrier
|
||||
|
||||
Solid or rigid particle groups are not inherently tunneling-proof. Particles
|
||||
traveling at high enough velocities may penetrate them. Barrier particles,
|
||||
used in conjunction with other particle types, provide particle groups
|
||||
with protection against tunneling. This functionality is useful when, for
|
||||
example, you want to ensure that liquid particles will not leak out of a
|
||||
container formed of wall particles.
|
||||
|
||||
Barrier particles only prevent penetration of the particle groups they inhabit.
|
||||
They cannot prevent particles from getting between groups of particles, even if
|
||||
the groups' positions make them look as if they are contiguous.
|
||||
|
||||
You can use barrier particles with elastic, spring, or wall particles.
|
||||
|
||||
The following example creates an impermeable group of wall particles:
|
||||
|
||||
`pd.flags = b2_wallParticle | b_barrierParticle;`
|
||||
`pd.groupFlags = b2_solidParticleGroup;`
|
||||
|
||||
### Zombie
|
||||
|
||||
Zombie particles are useful when you want efficiently to destroy multiple
|
||||
particles in a single step. All of the particles that you designate as zombies
|
||||
are destroyed at the same time, in a single iteration of the solver.
|
||||
Destroying
|
||||
particles in a batch, after designating them as zombies, yields better
|
||||
performance than destroying them one by one: Whereas destroying particles
|
||||
one-by-one takes (number of parti`cles) * (time per particle) to complete,
|
||||
destroying them all in a batch takes the same time as it would to destroy a
|
||||
single particle.<br/>
|
||||
In the following example, every other particle in a group is designated as a
|
||||
zombie, and will be destroyed in the next step of the solver. (For more
|
||||
information on the LiquidFun solver, see Chapter 1. Introduction.)
|
||||
|
||||
`b2ParticleGroup*group=
|
||||
m_particleSystem->CreateParticleGroup(pd);`<br/>
|
||||
`for (int32 i=0;i<group->GetParticleCount();i+=2)`<br/>
|
||||
`{`<br/>
|
||||
`group->GetFlagsBuffer()[i] |=`
|
||||
`b2_zombieParticle;`<br/>
|
||||
`}`
|
||||
|
||||
Note that you can assign multiple behaviors to a group or particle. Use
|
||||
the | ("bitwise OR") operator to chain behavior flags. For example, for a group:
|
||||
|
||||
`pd.groupFlags = b2_solidParticleGroup |
|
||||
b2_rigidParticleGroup;`
|
||||
|
||||
And for particles:
|
||||
|
||||
`pd.flags = b2_elasticParticle | b2_viscousParticle;`
|
||||
|
||||
To define a group combining a specific group behavior with a specific particle
|
||||
behavior, use two statements. For example:
|
||||
|
||||
`pd.flags = b2_elasticParticle;`<br/>
|
||||
`pd.groupFlags = b2_solidParticleGroup;`<br/>
|
||||
|
||||
<a name="pp">
|
||||
## Particle Properties
|
||||
|
||||
### Color
|
||||
|
||||
Set particle or particle-group color using the statement
|
||||
|
||||
`pd.color.Set(r, g, b, a);`
|
||||
|
||||
whose parameters set red, green, blue, and opacity, respectively. Each
|
||||
parameter takes a value of 0-255.
|
||||
|
||||
### Size
|
||||
|
||||
There are two points to keep in mind when using small particles. First, in the
|
||||
case of particle groups, particle size can affect performance. This is
|
||||
because particle size is inversely proportional to the number of particles
|
||||
generated to constitute a group. Having a large number of particles, in turn,
|
||||
can diminish performance.
|
||||
|
||||
Set particle size using the statement
|
||||
|
||||
`m_particleSystem->SetRadius(r);`
|
||||
|
||||
where `r` is a float32 value greater than 0.0f. Default particle radius is
|
||||
1.0f.
|
||||
|
||||
Small particles may also behave unpredictably (i.e., break conservation of
|
||||
momentum) in scenarios such as explosions. Slowing these particles down by
|
||||
reducing gravity scale can stabilize their behavior.
|
||||
|
||||
Set gravity scale using the statement
|
||||
|
||||
`m_particleSystem->SetGravityScale(g);`
|
||||
|
||||
where `g` is a `float32` value greater than 0.0f. Default gravity scale is
|
||||
1.0f.
|
||||
|
||||
It is worth noting that adjusting the number of particle iterations per solver
|
||||
step can also affect the effect of gravity on particles. Larger iteration sizes
|
||||
confer greater resistance to gravity. A common reason for increasing the number
|
||||
of particle-iterations is to prevent volume loss (i.e. compression) due to
|
||||
gravity.
|
||||
|
||||
### Position
|
||||
|
||||
Set particle or particle-group position using the statement
|
||||
|
||||
`pd.position.Set(x, y);`
|
||||
|
||||
where `x` and `y` are the world-coordinates of the translation of the
|
||||
particle
|
||||
group.
|
||||
|
||||
### Velocity
|
||||
|
||||
For discrete particles, set velocity using the statement
|
||||
|
||||
`pd.velocity.Set(x,y);`
|
||||
|
||||
where `x` is velocity along the x-axis, and `y` is velocity along the
|
||||
y-axis.<br/>
|
||||
For particle groups, set velocity using the statements
|
||||
|
||||
`pd.linearVelocity.Set(x,y);`<br/>
|
||||
`pd.angularVelocity = aV;`<br/>
|
||||
|
||||
where `x` is the group's velocity along the x-axis, `y` is velocity along the
|
||||
y-axis, and `aV` is the group's angular (i.e., rotational) velocity (expressed
|
||||
as radians per second).
|
||||
|
||||
### Angle (Groups Only)
|
||||
|
||||
This property applies only to rigid particle groups. It indicates the angle at
|
||||
which a group is tilted. Set angle with the statement
|
||||
|
||||
`pd.angle =checkout a;`
|
||||
|
||||
where `a` is the angle of tilt, expressed in radians. Left unspecified, the
|
||||
value defaults to 0.
|
||||
|
||||
### Strength (Groups Only)
|
||||
|
||||
Strength describes the cohesion of a group of particles. Set strength with the
|
||||
statement
|
||||
|
||||
`pd.strength = s;`<br/>
|
||||
|
||||
where `s` is a float32 value between 0.0 (least cohesive) and 1.0 (most
|
||||
cohesive). The default value is 1.0.
|
||||
|
||||
<a name="gl">
|
||||
## Rendering with OpenGL
|
||||
|
||||
The Particle module provides particularly efficient rendering via OpenGL.
|
||||
|
||||
Each type of particle property lives in a contiguous memory buffer. For
|
||||
example,
|
||||
all particles' position data live next door to one another, all color data
|
||||
live
|
||||
next door to one another, and so forth. Table 1 provides a visual
|
||||
representation
|
||||
of this storage.
|
||||
|
||||
**_Table 1. Memory Map of Particle Buffers_**
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>Particle 1</td>
|
||||
<td>Particle 2</td>
|
||||
<td>Particle 3</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Position</td>
|
||||
<td>x1,y1</td>
|
||||
<td>x2,y2</td>
|
||||
<td>x3,y3</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Address</td>
|
||||
<td>0x00001000</td>
|
||||
<td>0x00001008</td>
|
||||
<td>0x00001010</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Color</td>
|
||||
<td>r1,g1,b1,a1</td>
|
||||
<td>r2,g2,b2,a2</td>
|
||||
<td>r3,g3,b3,a3</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Address</td>
|
||||
<td>0x00002000</td>
|
||||
<td>0x00002004</td>
|
||||
<td>0x00002008</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
OpenGL can use these buffers directly in rendering.<br/>
|
||||
In this example, OpenGL 1.1 would use glVertexPointer and glColorPointer to
|
||||
get
|
||||
the values from memory. OpenGL 2.0 would use glVertexAttribPointer.<br/>
|
||||
OpenGL can be used to render either individual particles or particle groups.
|
||||
|
||||
<a name="sa">
|
||||
## Sample Applications
|
||||
|
||||
Among the samples included in the LiquidFun distribution are two applications
|
||||
that offer a quick look into the capabilities of the library.
|
||||
|
||||
Testbed includes a large number of demos that provide examples of different
|
||||
types of particle behavior. While some of the demos are "look only," others are
|
||||
interactive, allowing you to use your mouse or touchscreen to affect the
|
||||
behavior on screen.
|
||||
|
||||
Experimenting with each of the demos, and comparing their behavior against the
|
||||
source code, can provide useful insights into how different particles behave
|
||||
under various conditions. Testbed builds and runs on Android, MacOSX, Linux,
|
||||
and Windows.
|
||||
|
||||
EyeCandy is an Android-only application and is twofold in purpose: It provides
|
||||
a simple Android example of how to use LiquidFun; and, it seeks to inspire
|
||||
developers with its demonstration of the powerful liquid shaders it brings to
|
||||
mobile hardware.
|
||||
|
||||
When running the program, you can slosh the fluid around by changing the
|
||||
orientation of the Android device. You can also toggle bewteen shaders by
|
||||
tapping the screen.
|
||||
|
||||
|
||||
*This content is licensed under
|
||||
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
|
||||
For details and restrictions, please see the
|
||||
[Content License](md__content_license.html).*
|
||||
@@ -0,0 +1,178 @@
|
||||
# Loose Ends
|
||||
|
||||
[User Data](#ud)<br/>
|
||||
[Implicit Destruction](#id)<br/>
|
||||
[Pixels and Coordinate Systems](#pcs)<br/>
|
||||
|
||||
<a name="ud"></a>
|
||||
## User Data
|
||||
|
||||
The b2Fixture, b2Body, and b2Joint classes allow you to attach user data as a
|
||||
void pointer. This is handy when you are examining LiquidFun data structures
|
||||
and you want to determine how they relate to the objects in your game engine.
|
||||
|
||||
For example, it is typical to attach an actor pointer to the rigid body on
|
||||
that actor. This sets up a circular reference. If you have the actor, you can
|
||||
get the body. If you have the body, you can get the actor.
|
||||
|
||||
`GameActor* actor = GameCreateActor();`<br/>
|
||||
`b2BodyDef bodyDef;`<br/>
|
||||
`bodyDef.userData = actor;`<br/>
|
||||
`actor->body = box2Dworld->CreateBody(&bodyDef);`<br/>
|
||||
|
||||
Here are some examples of cases where you would need the user data:
|
||||
|
||||
* Applying damage to an actor using a collision result.
|
||||
|
||||
* Playing a scripted event if the player is inside an axis-aligned box.
|
||||
|
||||
* Accessing a game structure when LiquidFun notifies you that a joint is going
|
||||
to be destroyed.
|
||||
|
||||
Keep in mind that user data is optional and you can put anything in it.
|
||||
However, you should be consistent. For example, if you want to store an actor
|
||||
pointer on one body, you should keep an actor pointer on all bodies. Don't
|
||||
store an actor pointer on one body, and a foo pointer on another body. Casting
|
||||
an actor pointer to a foo pointer may lead to a crash.
|
||||
|
||||
User data pointers are NULL by default.
|
||||
|
||||
For fixtures you might consider defining a user data structure that lets you
|
||||
store game specific information, such as material type, effects hooks, sound
|
||||
hooks, etc.
|
||||
|
||||
`struct FixtureUserData`<br/>
|
||||
`{`<br/>
|
||||
` int materialIndex;`<br/>
|
||||
` . . .`<br/>
|
||||
`};`<br/>
|
||||
`FixtureUserData myData = new FixtureUserData;`<br/>
|
||||
`myData->materialIndex = 2;`<br/>
|
||||
`b2FixtureDef fixtureDef;`<br/>
|
||||
`fixtureDef.shape = &someShape;`<br/>
|
||||
`fixtureDef.userData = myData;`<br/>
|
||||
`b2Fixture* fixture = body->CreateFixture(&fixtureDef);`<br/>
|
||||
`. . .`<br/>
|
||||
`delete fixture->GetUserData();`<br/>
|
||||
`fixture->SetUserData(NULL);`<br/>
|
||||
`body->DestroyFixture(fixture);`<br/>
|
||||
|
||||
<a name="id"></a>
|
||||
## Implicit Destruction
|
||||
|
||||
LiquidFun doesn't use reference counting. So if you destroy a body it is
|
||||
really gone. Accessing a pointer to a destroyed body has undefined behavior.
|
||||
In other words, your program will likely crash and burn. To help fix these
|
||||
problems, the debug build memory manager fills destroyed entities with
|
||||
FDFDFDFD. This can help find problems more easily in some cases.
|
||||
|
||||
If you destroy a LiquidFun entity, it is up to you to make sure you remove all
|
||||
references to the destroyed object. This is easy if you only have a single
|
||||
reference to the entity. If you have multiple references, you might consider
|
||||
implementing a handle class to wrap the raw pointer.
|
||||
|
||||
Often when using LiquidFun you will create and destroy many bodies, shapes,
|
||||
and joints. Managing these entities is somewhat automated by LiquidFun. If you
|
||||
destroy a body then all associated shapes and joints are automatically
|
||||
destroyed. This is called implicit destruction.
|
||||
|
||||
When you destroy a body, all its attached shapes, joints, and contacts are
|
||||
destroyed. This is called implicit destruction. Any body connected to one of
|
||||
those joints and/or contacts is woken. This process is usually convenient.
|
||||
However, you must be aware of one crucial issue:
|
||||
|
||||
Caution
|
||||
|
||||
When a body is destroyed, all fixtures and joints attached to the body
|
||||
are automatically destroyed. You must nullify any pointers you have to those
|
||||
shapes and joints. Otherwise, your program will die horribly if you try to
|
||||
access or destroy those shapes or joints later.
|
||||
|
||||
|
||||
|
||||
To help you nullify your joint pointers, LiquidFun provides a listener class
|
||||
named b2DestructionListener that you can implement and provide to your world
|
||||
object. Then the world object will notify you when a joint is going to be
|
||||
implicitly destroyed
|
||||
|
||||
Note that there no notification when a joint or fixture is explicitly
|
||||
destroyed. In this case ownership is clear and you can perform the necessary
|
||||
cleanup on the spot. If you like, you can call your own implementation of
|
||||
b2DestructionListener to keep cleanup code centralized.
|
||||
|
||||
Implicit destruction is a great convenience in many cases. It can also make
|
||||
your program fall apart. You may store pointers to shapes and joints somewhere
|
||||
in your code. These pointers become orphaned when an associated body is
|
||||
destroyed. The situation becomes worse when you consider that joints are often
|
||||
created by a part of the code unrelated to management of the associated body.
|
||||
For example, the testbed creates a b2MouseJoint for interactive manipulation
|
||||
of bodies on the screen.
|
||||
|
||||
LiquidFun provides a callback mechanism to inform your application when
|
||||
implicit destruction occurs. This gives your application a chance to nullify
|
||||
the orphaned pointers. This callback mechanism is described later in this
|
||||
manual.
|
||||
|
||||
You can implement a b2DestructionListener that allows b2World to inform you
|
||||
when a shape or joint is implicitly destroyed because an associated body was
|
||||
destroyed. This will help prevent your code from accessing orphaned pointers.
|
||||
|
||||
`class MyDestructionListener : public
|
||||
b2DestructionListener`<br/>
|
||||
`{`<br/>
|
||||
`void SayGoodbye(b2Joint* joint)`<br/>
|
||||
`{`<br/>
|
||||
`// remove all
|
||||
references to joint.`<br/>
|
||||
`}`<br/>
|
||||
`};`<br/>
|
||||
|
||||
You can then register an instance of your destruction listener with your world
|
||||
object. You should do this during world initialization.
|
||||
|
||||
`myWorld->SetListener(myDestructionListener);`
|
||||
|
||||
<a name="pcs"></a>
|
||||
## Pixels and Coordinate Systems
|
||||
|
||||
Recall that LiquidFun uses MKS (meters, kilograms, and seconds) units and
|
||||
radians for angles. You may have trouble working with meters because your game
|
||||
is expressed in terms of pixels. To deal with this in the testbed I have the
|
||||
whole *game* work in meters and just use an OpenGL viewport transformation to
|
||||
scale the world into screen space.
|
||||
|
||||
`float lowerX = -25.0f, upperX = 25.0f, lowerY = -5.0f,
|
||||
upperY = 25.0f;`<br/>
|
||||
`gluOrtho2D(lowerX, upperX, lowerY, upperY);`<br/>
|
||||
|
||||
If your game must work in pixel units then you should convert your length
|
||||
units from pixels to meters when passing values from LiquidFun. Likewise you
|
||||
should convert the values received from LiquidFun from meters to pixels. This
|
||||
will improve the stability of the physics simulation.
|
||||
|
||||
You have to come up with a reasonable conversion factor. I suggest making this
|
||||
choice based on the size of your characters. Suppose you have determined to
|
||||
use 50 pixels per meter (because your character is 75 pixels tall). Then you
|
||||
can convert from pixels to meters using these formulas:
|
||||
|
||||
`xMeters = 0.02f * xPixels;`<br/>
|
||||
`yMeters = 0.02f * yPixels;`<br/>
|
||||
|
||||
In reverse:
|
||||
|
||||
`xPixels = 50.0f * xMeters;`<br/>
|
||||
`yPixels = 50.0f * yMeters;`<br/>
|
||||
|
||||
You should consider using MKS units in your game code and just convert to
|
||||
pixels when you render. This will simplify your game logic and reduce the
|
||||
chance for errors since the rendering conversion can be isolated to a small
|
||||
amount of code.
|
||||
|
||||
If you use a conversion factor, you should try tweaking it globally to make
|
||||
sure nothing breaks. You can also try adjusting it to improve stability.
|
||||
|
||||
|
||||
*This content is licensed under
|
||||
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
|
||||
For details and restrictions, please see the
|
||||
[Content License](md__content_license.html).*
|
||||
@@ -0,0 +1,28 @@
|
||||
# Debug Drawing
|
||||
|
||||
You can implement the b2DebugDraw class to get detailed drawing of the physics
|
||||
world. Here are the available entities:
|
||||
|
||||
* shape outlines
|
||||
|
||||
* joint connectivity
|
||||
|
||||
* broad-phase axis-aligned bounding boxes (AABBs)
|
||||
|
||||
* center of mass
|
||||
|
||||
<img align="center" src="image_24.png" alt="Debug drawing" height="306"
|
||||
width="431"><br/>
|
||||
This is the preferred method of drawing these physics entities, rather than
|
||||
accessing the data directly. The reason is that much of the necessary data is
|
||||
internal and subject to change.
|
||||
|
||||
The testbed draws physics entities using the debug draw facility and the
|
||||
contact listener, so it serves as the primary example of how to implement
|
||||
debug drawing as well as how to draw contact points.
|
||||
|
||||
|
||||
*This content is licensed under
|
||||
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
|
||||
For details and restrictions, please see the
|
||||
[Content License](md__content_license.html).*
|
||||
@@ -0,0 +1,33 @@
|
||||
## Limitations
|
||||
|
||||
LiquidFun uses several approximations to simulate rigid body physics
|
||||
efficiently. This brings some limitations.
|
||||
|
||||
Here are the current limitations:
|
||||
|
||||
1. Stacking heavy bodies on top of much lighter bodies is not stable.
|
||||
Stability degrades as the mass ratio passes 10:1.
|
||||
|
||||
2. Chains of bodies connected by joints may stretch if a lighter body is
|
||||
supporting a heavier body. For example, a wrecking ball connect to a chain of
|
||||
light weight bodies may not be stable. Stability degrades as the mass ratio
|
||||
passes 10:1.
|
||||
|
||||
3. There is typically around 0.5cm of slop in shape versus shape collision.
|
||||
|
||||
4. Continuous collision does not handle joints. So you may see joint
|
||||
stretching on fast moving objects.
|
||||
|
||||
5. LiquidFun uses the symplectic Euler integration scheme. It does not
|
||||
reproduce parabolic motion of projectiles and has only first-order accuracy.
|
||||
However it is fast and has good stability.
|
||||
|
||||
6. LiquidFun uses an iterative solver to provide real-time performance. You
|
||||
will not get precisely rigid collisions or pixel perfect accuracy. Increasing
|
||||
the iterations will improve accuracy.
|
||||
|
||||
|
||||
*This content is licensed under
|
||||
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
|
||||
For details and restrictions, please see the
|
||||
[Content License](md__content_license.html).*
|
||||
@@ -0,0 +1,13 @@
|
||||
# References
|
||||
|
||||
Erin Catto's GDC Tutorials:<br/>
|
||||
[http://code.google.com/p/box2d/downloads/list](http://code.google.com/p/box2d/downloads/list)<br/>
|
||||
_Collision Detection in Interactive 3D Environments,_ Gino van den Bergen,
|
||||
2004<br/>
|
||||
_Real-Time Collision Detection,_ Christer Ericson, 2005
|
||||
|
||||
|
||||
*This content is licensed under
|
||||
[Creative Commons Attribution 4.0](http://creativecommons.org/licenses/by/4.0/legalcode).
|
||||
For details and restrictions, please see the
|
||||
[Content License](md__content_license.html).*
|
||||
@@ -0,0 +1,11 @@
|
||||
# Content License
|
||||
|
||||
Portions of this documentation are modifications based on work
|
||||
created and shared by Erin Catto and used according to terms
|
||||
described in the
|
||||
[Creative Commons 4.0 Attribution License](http://creativecommons.org/licenses/by/4.0/legalcode)
|
||||
|
||||
Copyright © 2007-2011 Erin Catto
|
||||
|
||||
Copyright © 2013-2014 Fun Propulsion Labs at Google
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# Logo
|
||||
|
||||
Please feel free to make fair use the LiquidFun logo in connection with any
|
||||
implementations (in your splash screens or documentation, for example).
|
||||
There is no requirement to include the logo, but we appreciate your
|
||||
acknowledgement. We only ask that you avoid changing the proportions of the
|
||||
logo or otherwise modifying it, and that you avoid using the logo in a way
|
||||
that suggests your implementation is developed by, sponsored by, or affiliated
|
||||
with Fun Propulsion Labs or Google. (For example, you shouldn't use the
|
||||
LiquidFun logo as your app icon, and you shouldn't use it more prominently
|
||||
than your own logos or icons.)
|
||||
|
||||

|
||||
|
||||
* [Logo in bitmap format](liquidfun-logo.png)
|
||||
* [Logo in vector format](liquidfun-logo.ai)
|
||||
|
||||
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 341 KiB |