Liquidfun links
This commit is contained in:
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Google, Inc.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include <Box2D/Particle/b2Particle.h>
|
||||
#include <Box2D/Common/b2Draw.h>
|
||||
|
||||
#define B2PARTICLECOLOR_BITS_PER_COMPONENT (sizeof(uint8) << 3)
|
||||
// Maximum value of a b2ParticleColor component.
|
||||
#define B2PARTICLECOLOR_MAX_VALUE \
|
||||
((1U << B2PARTICLECOLOR_BITS_PER_COMPONENT) - 1)
|
||||
|
||||
/// Number of bits used to store each b2ParticleColor component.
|
||||
const uint8 b2ParticleColor::k_bitsPerComponent =
|
||||
B2PARTICLECOLOR_BITS_PER_COMPONENT;
|
||||
const float32 b2ParticleColor::k_maxValue = (float)B2PARTICLECOLOR_MAX_VALUE;
|
||||
const float32 b2ParticleColor::k_inverseMaxValue =
|
||||
1.0f / (float)B2PARTICLECOLOR_MAX_VALUE;
|
||||
|
||||
b2ParticleColor b2ParticleColor_zero(0, 0, 0, 0);
|
||||
|
||||
b2ParticleColor::b2ParticleColor(const b2Color& color)
|
||||
{
|
||||
Set(color);
|
||||
}
|
||||
|
||||
b2Color b2ParticleColor::GetColor() const
|
||||
{
|
||||
return b2Color(k_inverseMaxValue * r,
|
||||
k_inverseMaxValue * g,
|
||||
k_inverseMaxValue * b);
|
||||
}
|
||||
|
||||
void b2ParticleColor::Set(const b2Color& color)
|
||||
{
|
||||
Set((uint8)(k_maxValue * color.r),
|
||||
(uint8)(k_maxValue * color.g),
|
||||
(uint8)(k_maxValue * color.b),
|
||||
B2PARTICLECOLOR_MAX_VALUE);
|
||||
}
|
||||
|
||||
int32 b2CalculateParticleIterations(
|
||||
float32 gravity, float32 radius, float32 timeStep)
|
||||
{
|
||||
// In some situations you may want more particle iterations than this,
|
||||
// but to avoid excessive cycle cost, don't recommend more than this.
|
||||
const int32 B2_MAX_RECOMMENDED_PARTICLE_ITERATIONS = 8;
|
||||
const float32 B2_RADIUS_THRESHOLD = 0.01f;
|
||||
int32 iterations =
|
||||
(int32) ceilf(b2Sqrt(gravity / (B2_RADIUS_THRESHOLD * radius)) * timeStep);
|
||||
return b2Clamp(iterations, 1, B2_MAX_RECOMMENDED_PARTICLE_ITERATIONS);
|
||||
}
|
||||
+366
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Google, Inc.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#ifndef B2_PARTICLE
|
||||
#define B2_PARTICLE
|
||||
|
||||
#include <Box2D/Common/b2Math.h>
|
||||
#include <Box2D/Common/b2Settings.h>
|
||||
#include <Box2D/Common/b2IntrusiveList.h>
|
||||
|
||||
struct b2Color;
|
||||
class b2ParticleGroup;
|
||||
|
||||
/// @file
|
||||
|
||||
/// The particle type. Can be combined with the | operator.
|
||||
enum b2ParticleFlag
|
||||
{
|
||||
/// Water particle.
|
||||
b2_waterParticle = 0,
|
||||
/// Removed after next simulation step.
|
||||
b2_zombieParticle = 1 << 1,
|
||||
/// Zero velocity.
|
||||
b2_wallParticle = 1 << 2,
|
||||
/// With restitution from stretching.
|
||||
b2_springParticle = 1 << 3,
|
||||
/// With restitution from deformation.
|
||||
b2_elasticParticle = 1 << 4,
|
||||
/// With viscosity.
|
||||
b2_viscousParticle = 1 << 5,
|
||||
/// Without isotropic pressure.
|
||||
b2_powderParticle = 1 << 6,
|
||||
/// With surface tension.
|
||||
b2_tensileParticle = 1 << 7,
|
||||
/// Mix color between contacting particles.
|
||||
b2_colorMixingParticle = 1 << 8,
|
||||
/// Call b2DestructionListener on destruction.
|
||||
b2_destructionListenerParticle = 1 << 9,
|
||||
/// Prevents other particles from leaking.
|
||||
b2_barrierParticle = 1 << 10,
|
||||
/// Less compressibility.
|
||||
b2_staticPressureParticle = 1 << 11,
|
||||
/// Makes pairs or triads with other particles.
|
||||
b2_reactiveParticle = 1 << 12,
|
||||
/// With high repulsive force.
|
||||
b2_repulsiveParticle = 1 << 13,
|
||||
/// Call b2ContactListener when this particle is about to interact with
|
||||
/// a rigid body or stops interacting with a rigid body.
|
||||
/// This results in an expensive operation compared to using
|
||||
/// b2_fixtureContactFilterParticle to detect collisions between
|
||||
/// particles.
|
||||
b2_fixtureContactListenerParticle = 1 << 14,
|
||||
/// Call b2ContactListener when this particle is about to interact with
|
||||
/// another particle or stops interacting with another particle.
|
||||
/// This results in an expensive operation compared to using
|
||||
/// b2_particleContactFilterParticle to detect collisions between
|
||||
/// particles.
|
||||
b2_particleContactListenerParticle = 1 << 15,
|
||||
/// Call b2ContactFilter when this particle interacts with rigid bodies.
|
||||
b2_fixtureContactFilterParticle = 1 << 16,
|
||||
/// Call b2ContactFilter when this particle interacts with other
|
||||
/// particles.
|
||||
b2_particleContactFilterParticle = 1 << 17,
|
||||
};
|
||||
|
||||
/// Small color object for each particle
|
||||
class b2ParticleColor
|
||||
{
|
||||
public:
|
||||
b2ParticleColor() {}
|
||||
/// Constructor with four elements: r (red), g (green), b (blue), and a
|
||||
/// (opacity).
|
||||
/// Each element can be specified 0 to 255.
|
||||
b2Inline b2ParticleColor(uint8 r, uint8 g, uint8 b, uint8 a)
|
||||
{
|
||||
Set(r, g, b, a);
|
||||
}
|
||||
|
||||
/// Constructor that initializes the above four elements with the value of
|
||||
/// the b2Color object.
|
||||
b2ParticleColor(const b2Color& color);
|
||||
|
||||
/// True when all four color elements equal 0. When true, a particle color
|
||||
/// buffer isn't allocated by CreateParticle().
|
||||
///
|
||||
bool IsZero() const
|
||||
{
|
||||
return !r && !g && !b && !a;
|
||||
}
|
||||
|
||||
/// Used internally to convert the value of b2Color.
|
||||
///
|
||||
b2Color GetColor() const;
|
||||
|
||||
/// Sets color for current object using the four elements described above.
|
||||
///
|
||||
b2Inline void Set(uint8 r_, uint8 g_, uint8 b_, uint8 a_)
|
||||
{
|
||||
r = r_;
|
||||
g = g_;
|
||||
b = b_;
|
||||
a = a_;
|
||||
}
|
||||
|
||||
/// Initializes the object with the value of the b2Color.
|
||||
///
|
||||
void Set(const b2Color& color);
|
||||
|
||||
/// Assign a b2ParticleColor to this instance.
|
||||
b2ParticleColor& operator = (const b2ParticleColor &color)
|
||||
{
|
||||
Set(color.r, color.g, color.b, color.a);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Multiplies r, g, b, a members by s where s is a value between 0.0
|
||||
/// and 1.0.
|
||||
b2ParticleColor& operator *= (float32 s)
|
||||
{
|
||||
Set((uint8)(r * s), (uint8)(g * s), (uint8)(b * s), (uint8)(a * s));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Scales r, g, b, a members by s where s is a value between 0 and 255.
|
||||
b2ParticleColor& operator *= (uint8 s)
|
||||
{
|
||||
// 1..256 to maintain the complete dynamic range.
|
||||
const int32 scale = (int32)s + 1;
|
||||
Set((uint8)(((int32)r * scale) >> k_bitsPerComponent),
|
||||
(uint8)(((int32)g * scale) >> k_bitsPerComponent),
|
||||
(uint8)(((int32)b * scale) >> k_bitsPerComponent),
|
||||
(uint8)(((int32)a * scale) >> k_bitsPerComponent));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Scales r, g, b, a members by s returning the modified b2ParticleColor.
|
||||
b2ParticleColor operator * (float32 s) const
|
||||
{
|
||||
return MultiplyByScalar(s);
|
||||
}
|
||||
|
||||
/// Scales r, g, b, a members by s returning the modified b2ParticleColor.
|
||||
b2ParticleColor operator * (uint8 s) const
|
||||
{
|
||||
return MultiplyByScalar(s);
|
||||
}
|
||||
|
||||
/// Add two colors. This is a non-saturating addition so values
|
||||
/// overflows will wrap.
|
||||
b2Inline b2ParticleColor& operator += (const b2ParticleColor &color)
|
||||
{
|
||||
r += color.r;
|
||||
g += color.g;
|
||||
b += color.b;
|
||||
a += color.a;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Add two colors. This is a non-saturating addition so values
|
||||
/// overflows will wrap.
|
||||
b2ParticleColor operator + (const b2ParticleColor &color) const
|
||||
{
|
||||
b2ParticleColor newColor(*this);
|
||||
newColor += color;
|
||||
return newColor;
|
||||
}
|
||||
|
||||
/// Subtract a color from this color. This is a subtraction without
|
||||
/// saturation so underflows will wrap.
|
||||
b2Inline b2ParticleColor& operator -= (const b2ParticleColor &color)
|
||||
{
|
||||
r -= color.r;
|
||||
g -= color.g;
|
||||
b -= color.b;
|
||||
a -= color.a;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Subtract a color from this color returning the result. This is a
|
||||
/// subtraction without saturation so underflows will wrap.
|
||||
b2ParticleColor operator - (const b2ParticleColor &color) const
|
||||
{
|
||||
b2ParticleColor newColor(*this);
|
||||
newColor -= color;
|
||||
return newColor;
|
||||
}
|
||||
|
||||
/// Compare this color with the specified color.
|
||||
bool operator == (const b2ParticleColor &color) const
|
||||
{
|
||||
return r == color.r && g == color.g && b == color.b && a == color.a;
|
||||
}
|
||||
|
||||
/// Mix mixColor with this color using strength to control how much of
|
||||
/// mixColor is mixed with this color and vice versa. The range of
|
||||
/// strength is 0..128 where 0 results in no color mixing and 128 results
|
||||
/// in an equal mix of both colors. strength 0..128 is analogous to an
|
||||
/// alpha channel value between 0.0f..0.5f.
|
||||
b2Inline void Mix(b2ParticleColor * const mixColor, const int32 strength)
|
||||
{
|
||||
MixColors(this, mixColor, strength);
|
||||
}
|
||||
|
||||
/// Mix colorA with colorB using strength to control how much of
|
||||
/// colorA is mixed with colorB and vice versa. The range of
|
||||
/// strength is 0..128 where 0 results in no color mixing and 128 results
|
||||
/// in an equal mix of both colors. strength 0..128 is analogous to an
|
||||
/// alpha channel value between 0.0f..0.5f.
|
||||
static b2Inline void MixColors(b2ParticleColor * const colorA,
|
||||
b2ParticleColor * const colorB,
|
||||
const int32 strength)
|
||||
{
|
||||
const uint8 dr = (uint8)((strength * (colorB->r - colorA->r)) >>
|
||||
k_bitsPerComponent);
|
||||
const uint8 dg = (uint8)((strength * (colorB->g - colorA->g)) >>
|
||||
k_bitsPerComponent);
|
||||
const uint8 db = (uint8)((strength * (colorB->b - colorA->b)) >>
|
||||
k_bitsPerComponent);
|
||||
const uint8 da = (uint8)((strength * (colorB->a - colorA->a)) >>
|
||||
k_bitsPerComponent);
|
||||
colorA->r += dr;
|
||||
colorA->g += dg;
|
||||
colorA->b += db;
|
||||
colorA->a += da;
|
||||
colorB->r -= dr;
|
||||
colorB->g -= dg;
|
||||
colorB->b -= db;
|
||||
colorB->a -= da;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Generalization of the multiply operator using a scalar in-place
|
||||
/// multiplication.
|
||||
template <typename T>
|
||||
b2ParticleColor MultiplyByScalar(T s) const
|
||||
{
|
||||
b2ParticleColor color(*this);
|
||||
color *= s;
|
||||
return color;
|
||||
}
|
||||
|
||||
public:
|
||||
uint8 r, g, b, a;
|
||||
|
||||
protected:
|
||||
/// Maximum value of a b2ParticleColor component.
|
||||
static const float32 k_maxValue;
|
||||
/// 1.0 / k_maxValue.
|
||||
static const float32 k_inverseMaxValue;
|
||||
/// Number of bits used to store each b2ParticleColor component.
|
||||
static const uint8 k_bitsPerComponent;
|
||||
};
|
||||
|
||||
extern b2ParticleColor b2ParticleColor_zero;
|
||||
|
||||
/// A particle definition holds all the data needed to construct a particle.
|
||||
/// You can safely re-use these definitions.
|
||||
struct b2ParticleDef
|
||||
{
|
||||
b2ParticleDef()
|
||||
{
|
||||
flags = 0;
|
||||
position = b2Vec2_zero;
|
||||
velocity = b2Vec2_zero;
|
||||
color = b2ParticleColor_zero;
|
||||
lifetime = 0.0f;
|
||||
userData = NULL;
|
||||
group = NULL;
|
||||
}
|
||||
|
||||
#if LIQUIDFUN_EXTERNAL_LANGUAGE_API
|
||||
/// Set position with direct floats
|
||||
void SetPosition(float32 x, float32 y);
|
||||
|
||||
/// Set color with direct ints.
|
||||
void SetColor(int32 r, int32 g, int32 b, int32 a);
|
||||
#endif // LIQUIDFUN_EXTERNAL_LANGUAGE_API
|
||||
|
||||
/// \brief Specifies the type of particle (see #b2ParticleFlag).
|
||||
///
|
||||
/// A particle may be more than one type.
|
||||
/// Multiple types are chained by logical sums, for example:
|
||||
/// pd.flags = b2_elasticParticle | b2_viscousParticle
|
||||
uint32 flags;
|
||||
|
||||
/// The world position of the particle.
|
||||
b2Vec2 position;
|
||||
|
||||
/// The linear velocity of the particle in world co-ordinates.
|
||||
b2Vec2 velocity;
|
||||
|
||||
/// The color of the particle.
|
||||
b2ParticleColor color;
|
||||
|
||||
/// Lifetime of the particle in seconds. A value <= 0.0f indicates a
|
||||
/// particle with infinite lifetime.
|
||||
float32 lifetime;
|
||||
|
||||
/// Use this to store application-specific body data.
|
||||
void* userData;
|
||||
|
||||
/// An existing particle group to which the particle will be added.
|
||||
b2ParticleGroup* group;
|
||||
|
||||
};
|
||||
|
||||
/// A helper function to calculate the optimal number of iterations.
|
||||
int32 b2CalculateParticleIterations(
|
||||
float32 gravity, float32 radius, float32 timeStep);
|
||||
|
||||
/// Handle to a particle. Particle indices are ephemeral: the same index might
|
||||
/// refer to a different particle, from frame-to-frame. If you need to keep a
|
||||
/// reference to a particular particle across frames, you should acquire a
|
||||
/// b2ParticleHandle. Use #b2ParticleSystem::GetParticleHandleFromIndex() to
|
||||
/// retrieve the b2ParticleHandle of a particle from the particle system.
|
||||
class b2ParticleHandle : public b2TypedIntrusiveListNode<b2ParticleHandle>
|
||||
{
|
||||
// Allow b2ParticleSystem to use SetIndex() to associate particle handles
|
||||
// with particle indices.
|
||||
friend class b2ParticleSystem;
|
||||
|
||||
public:
|
||||
/// Initialize the index associated with the handle to an invalid index.
|
||||
b2ParticleHandle() : m_index(b2_invalidParticleIndex) { }
|
||||
/// Empty destructor.
|
||||
~b2ParticleHandle() { }
|
||||
|
||||
/// Get the index of the particle associated with this handle.
|
||||
int32 GetIndex() const { return m_index; }
|
||||
|
||||
private:
|
||||
/// Set the index of the particle associated with this handle.
|
||||
void SetIndex(int32 index) { m_index = index; }
|
||||
|
||||
private:
|
||||
// Index of the particle within the particle system.
|
||||
int32 m_index;
|
||||
};
|
||||
|
||||
#if LIQUIDFUN_EXTERNAL_LANGUAGE_API
|
||||
inline void b2ParticleDef::SetPosition(float32 x, float32 y)
|
||||
{
|
||||
position.Set(x, y);
|
||||
}
|
||||
|
||||
inline void b2ParticleDef::SetColor(int32 r, int32 g, int32 b, int32 a)
|
||||
{
|
||||
color.Set((uint8)r, (uint8)g, (uint8)b, (uint8)a);
|
||||
}
|
||||
#endif // LIQUIDFUN_EXTERNAL_LANGUAGE_API
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Google, Inc.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include <Box2D/Particle/b2ParticleAssembly.h>
|
||||
#include <Box2D/Particle/b2ParticleSystem.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
// Helper function, called from assembly routine.
|
||||
void GrowParticleContactBuffer(
|
||||
b2GrowableBuffer<b2ParticleContact>& contacts)
|
||||
{
|
||||
// Set contacts.count = capacity instead of count because there are
|
||||
// items past the end of the array waiting to be post-processed.
|
||||
// We must maintain the entire contacts array.
|
||||
// TODO: It would be better to have the items awaiting post-processing
|
||||
// in their own array on the stack.
|
||||
contacts.SetCount(contacts.GetCapacity());
|
||||
contacts.Grow();
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Google, Inc.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#ifndef B2_PARTICLE_ASSEMBLY_H
|
||||
#define B2_PARTICLE_ASSEMBLY_H
|
||||
|
||||
#include <Box2D/Common/b2GrowableBuffer.h>
|
||||
#include <Box2D/Common/b2Math.h>
|
||||
|
||||
|
||||
struct b2ParticleContact;
|
||||
|
||||
struct FindContactCheck
|
||||
{
|
||||
uint16 particleIndex;
|
||||
uint16 comparatorIndex;
|
||||
};
|
||||
|
||||
struct FindContactInput
|
||||
{
|
||||
uint32 proxyIndex;
|
||||
b2Vec2 position;
|
||||
};
|
||||
|
||||
enum { NUM_V32_SLOTS = 4 };
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern int CalculateTags_Simd(const b2Vec2* positions,
|
||||
int count,
|
||||
const float& inverseDiameter,
|
||||
uint32* outTags);
|
||||
|
||||
extern void FindContactsFromChecks_Simd(
|
||||
const FindContactInput* reordered,
|
||||
const FindContactCheck* checks,
|
||||
int numChecks,
|
||||
const float& particleDiameterSq,
|
||||
const float& particleDiameterInv,
|
||||
const uint32* flags,
|
||||
b2GrowableBuffer<b2ParticleContact>& contacts);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,441 @@
|
||||
@
|
||||
@ Copyright (c) 2014 Google, Inc.
|
||||
@
|
||||
@ This software is provided 'as-is', without any express or implied
|
||||
@ warranty. In no event will the authors be held liable for any damages
|
||||
@ arising from the use of this software.
|
||||
@ Permission is granted to anyone to use this software for any purpose,
|
||||
@ including commercial applications, and to alter it and redistribute it
|
||||
@ freely, subject to the following restrictions:
|
||||
@ 1. The origin of this software must not be misrepresented; you must not
|
||||
@ claim that you wrote the original software. If you use this software
|
||||
@ in a product, an acknowledgment in the product documentation would be
|
||||
@ appreciated but is not required.
|
||||
@ 2. Altered source versions must be plainly marked as such, and must not be
|
||||
@ misrepresented as being the original software.
|
||||
@ 3. This notice may not be removed or altered from any source distribution.
|
||||
@
|
||||
.text
|
||||
.syntax unified
|
||||
|
||||
.balign 4
|
||||
.global CalculateTags_Simd
|
||||
.thumb_func
|
||||
|
||||
CalculateTags_Simd:
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
@
|
||||
@ int CalculateTags_Simd(const b2Vec2* positions,
|
||||
@ int count,
|
||||
@ const float& inverseDiameter,
|
||||
@ uint32* outTags)
|
||||
@
|
||||
@ r0: *positions
|
||||
@ r1: count
|
||||
@ r2: &inverseDiameter
|
||||
@ r3: *outTags
|
||||
@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
|
||||
@ q0 == x
|
||||
@ q1 == y
|
||||
@ q2 ==
|
||||
@ q3 ==
|
||||
@ q4 ==
|
||||
@ q5 ==
|
||||
@ q6 ==
|
||||
@ q7 ==
|
||||
@ q8 ==
|
||||
@ q9 ==
|
||||
@ q10 ==
|
||||
@ q11 ==
|
||||
@ q12 == inverseDiameter
|
||||
@ q13 == xScale
|
||||
@ q14 == xOffset
|
||||
@ q15 == yOffset
|
||||
|
||||
@ Load constants. Literals are > 32, so must load as integers first.
|
||||
vld1.f32 {d24[],d25[]}, [r2] @ q12 = inverseDiameter
|
||||
vmov.i32 q13, #0x100 @ q13 = xScale = 1 << 8
|
||||
vmov.i32 q14, #0x80000 @ q14 = xOffset = (1 << 8) * (1 << 11)
|
||||
@ = (1 << 19) = 524288
|
||||
vmov.i32 q15, #0x800 @ q15 = xScale = 1 << 11 = 2048
|
||||
vcvt.f32.u32 q13, q13 @ convert to float
|
||||
vcvt.f32.u32 q14, q14
|
||||
vcvt.f32.u32 q15, q15
|
||||
|
||||
@ Calculate tags four at a time, from positions.
|
||||
.L_CalculateTags_MainLoop:
|
||||
@ We consume 32-bytes per iteration, so prefetch 4 iterations ahead.
|
||||
@ TODO: experiment with different prefetch lengths on different
|
||||
@ architectures.
|
||||
pld [r0, #128] @ Prefetch position data
|
||||
|
||||
@ {q0, q1} == xPosition and yPosition
|
||||
@ Four values in each. q0 = (x0, x1, x2, x3)
|
||||
vld2.f32 {q0, q1}, [r0]! @ Read in positions; increment ptr
|
||||
|
||||
@ Calculate tags four at a time.
|
||||
vmul.f32 q0, q0, q12 @ q0 = x = xPosition * inverseDiameter
|
||||
vmul.f32 q1, q1, q12 @ q1 = y = yPosition * inverseDiameter
|
||||
vmul.f32 q0, q0, q13 @ q0 = x * xScale
|
||||
vadd.f32 q1, q1, q15 @ q1 = y + yOffset
|
||||
vadd.f32 q0, q0, q14 @ q0 = x * xScale + xOffset
|
||||
vcvt.u32.f32 q1, q1 @ q1 = (uint32)(y + yOffset)
|
||||
vcvt.u32.f32 q0, q0 @ q0 = (uint32)(x * xScale + xOffset)
|
||||
vsli.u32 q0, q1, #20 @ q0 = tag
|
||||
@ = ((uint32)(y + yOffset) <<yShift)
|
||||
@ + (uint32)(xScale * x + xOffset)
|
||||
|
||||
@ Decrement loop counter; sets the 'gt' flag used in 'bgt' below.
|
||||
@ Pipelining is best if there are instructions between the 'subs' and
|
||||
@ 'bgt' instructions, since it takes a few cycles for the result of
|
||||
@ 'subs' to propegate to the flags register.
|
||||
subs r1, r1, #4
|
||||
|
||||
@ Write out, ignoring index.
|
||||
pld [r3, #64] @ Prefetch output tag array
|
||||
vst1.f32 {q0}, [r3]! @ write out tags; increment ptr
|
||||
|
||||
bgt .L_CalculateTags_MainLoop
|
||||
|
||||
.L_CalculateTags_Return:
|
||||
bx lr
|
||||
|
||||
|
||||
|
||||
.balign 4
|
||||
.thumb_func
|
||||
@
|
||||
@ Once four contacts have been found, calculate their weights and
|
||||
@ normals (using SIMD, so all at once).
|
||||
@
|
||||
@ Also, grab their flags from the flags buffer, and OR them together.
|
||||
@ This flag grabbing is slow because we access the flag buffer in a
|
||||
@ random order. We use prefetch instructions 'pld' to minimize the
|
||||
@ cost of cache misses.
|
||||
@
|
||||
FindContacts_PostProcess:
|
||||
@ Preload first four flag addresses into cache.
|
||||
@ Note: hardware only has four preload slots.
|
||||
ldrh r9, [r4]
|
||||
ldrh r10, [r4, #2]
|
||||
ldrh r11, [r4, #16]
|
||||
ldrh r12, [r4, #18]
|
||||
pld [r7, r9, lsl #2]
|
||||
pld [r7, r10, lsl #2]
|
||||
pld [r7, r11, lsl #2]
|
||||
pld [r7, r12, lsl #2]
|
||||
|
||||
@ q0 = packedIndices -- indices output to b2ParticleContact
|
||||
@ q1 = distBtParticlesSq -- will be used to calculate weight
|
||||
@ q2 = diffX -- will be used to calculate normal
|
||||
@ q3 = diffY -- will be used to calculate normal
|
||||
add r8, r4, #32
|
||||
vld4.f32 {d0, d2, d4, d6}, [r4]
|
||||
vld4.f32 {d1, d3, d5, d7}, [r8]
|
||||
|
||||
@ Use distSq to estimate 1 / dist.
|
||||
vrsqrte.f32 q8, q1 @ q8 = 1 / dist -- (rough estimate)
|
||||
vmul.f32 q9, q8, q1 @ q9 = 1 / dist * distSq -- (appr 'dist')
|
||||
vrsqrts.f32 q9, q9, q8 @ q9 = (3 - 1/dist * dist) / 2 -- (error)
|
||||
vmul.f32 q8, q8, q9 @ q8 = (error) / dist -- (estimate)
|
||||
vcgt.f32 q9, q8, #0 @ q8 = 1 / dist > 0 (true if not NaN)
|
||||
vand q8, q8, q9 @ q8 = 1 / dist if valid, or 0 if NaN
|
||||
|
||||
@ Since we expand the output to include 'weight', we need to preserve
|
||||
@ subsequent contacts. Note that there may be up to 7 contacts waiting
|
||||
@ to be post-processed, since we output contacts in up-to groups of 4.
|
||||
add r8, r4, #64
|
||||
vldmia r8, {q9, q10, q11}
|
||||
|
||||
@ Load first four flags, 'or' them in pairs, then write to destination.
|
||||
ldr r9, [r7, r9, lsl #2]
|
||||
ldr r10, [r7, r10, lsl #2]
|
||||
ldr r11, [r7, r11, lsl #2]
|
||||
ldr r12, [r7, r12, lsl #2]
|
||||
orr r9, r9, r10
|
||||
orr r11, r11, r12
|
||||
str r9, [r4, #16]
|
||||
str r11, [r4, #36]
|
||||
|
||||
@ Preload the next four flags into cache.
|
||||
ldrh r9, [r4, #32]
|
||||
ldrh r10, [r4, #34]
|
||||
ldrh r11, [r4, #48]
|
||||
ldrh r12, [r4, #50]
|
||||
pld [r7, r9, lsl #2]
|
||||
pld [r7, r10, lsl #2]
|
||||
pld [r7, r11, lsl #2]
|
||||
pld [r7, r12, lsl #2]
|
||||
|
||||
@ Calculate normal and weight.
|
||||
vmul.f32 q1, q1, q8 @ q1 = distSq / dist = dist
|
||||
vmul.f32 q2, q2, q8 @ q2 = normX = diffX / dist
|
||||
vmul.f32 q1, q1, q14 @ q1 = dist / diameter
|
||||
vmul.f32 q3, q3, q8 @ q3 = normY = diffY / dist
|
||||
vsub.f32 q1, q12, q1 @ q1 = weight = 1 - dist / diameter
|
||||
|
||||
@ Store again, making room for 'weight' member variable this time.
|
||||
@ TODO OPT: Interleave with 'or' instructions below.
|
||||
mov r8, #20 @ r8 = 20 = sizeof(b2ParticleContact)
|
||||
vst4.f32 {d0[0], d2[0], d4[0], d6[0]}, [r4], r8
|
||||
vst4.f32 {d0[1], d2[1], d4[1], d6[1]}, [r4], r8
|
||||
vst4.f32 {d1[0], d3[0], d5[0], d7[0]}, [r4], r8
|
||||
vst4.f32 {d1[1], d3[1], d5[1], d7[1]}, [r4], r8
|
||||
mov r8, #12 @ r8 = 12 = sizeof(FindContactInput)
|
||||
|
||||
@ Load next four flags, 'or' them in pairs, then write to destination.
|
||||
ldr r9, [r7, r9, lsl #2]
|
||||
ldr r10, [r7, r10, lsl #2]
|
||||
ldr r11, [r7, r11, lsl #2]
|
||||
ldr r12, [r7, r12, lsl #2]
|
||||
orr r9, r9, r10
|
||||
orr r11, r11, r12
|
||||
str r9, [r4, #-24]
|
||||
str r11, [r4, #-4]
|
||||
|
||||
@ Update output pointers. Since we output 4 contacts, and added 4 bytes
|
||||
@ for 'weight' on each contact, the output pointer must be advanced by
|
||||
@ 16 bytes.
|
||||
add r3, r3, #16
|
||||
add r5, r5, #4 @ numContacts += 4
|
||||
|
||||
@ Restore subsequent contacts. That is, contacts that have yet to be
|
||||
@ post-processed.
|
||||
vstmia r4, {q9, q10, q11}
|
||||
|
||||
bx lr
|
||||
|
||||
|
||||
@ When used with the 'vtbl' instruction, grabs the first byte of every
|
||||
@ word, and places it in the first word. Fills the second word with 0s.
|
||||
@ For example, (0xFFFFFFFF, 0x00000000, 0x00000000, 0xFFFFFFFF)
|
||||
@ ==> (0xFF0000FF, 0x00000000)
|
||||
CONST_IS_CLOSE_TABLE_INDICES:
|
||||
.byte 0
|
||||
.byte 4
|
||||
.byte 8
|
||||
.byte 12
|
||||
.byte 0xFF
|
||||
.byte 0xFF
|
||||
.byte 0xFF
|
||||
.byte 0xFF
|
||||
|
||||
|
||||
.balign 4
|
||||
.global FindContactsFromChecks_Simd
|
||||
.thumb_func
|
||||
FindContactsFromChecks_Simd:
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
@
|
||||
@ void FindContactsFromChecks_Simd(
|
||||
@ const FindContactInput* reordered,
|
||||
@ const FindContactCheck* checks,
|
||||
@ int numChecks,
|
||||
@ const float& particleDiameterSq,
|
||||
@ const float& particleDiameterInv,
|
||||
@ const uint32* flags,
|
||||
@ b2GrowableBuffer<b2ParticleContact>& contacts)
|
||||
@
|
||||
@ Parameters
|
||||
@ r0: *reordered
|
||||
@ r1: *checks
|
||||
@ r2: numChecks
|
||||
@ r3: particleDiameterSq
|
||||
@ [sp]: particleDiameterInv
|
||||
@ [sp+4]: *flags
|
||||
@ [sp+8]: contacts
|
||||
@
|
||||
@ Persistent Variables
|
||||
@ r0: *reordered (constant)
|
||||
@ r1: *checks (advance once per iteration)
|
||||
@ r2: numChecks (decrement once per iteration)
|
||||
@ r3: *out <-- next free entry of outContacts array
|
||||
@ r4: *postProcess <-- entry on-deck to be post-processed
|
||||
@ r5: numContacts
|
||||
@ r6: maxSafeContacts
|
||||
@ r7: *flags (constant)
|
||||
@ r8: 20 = sizeof(b2ParticleContact), or
|
||||
@ 12 = sizeof(FindContactInput) (constants)
|
||||
@
|
||||
@ Scratch Variables
|
||||
@ r9:
|
||||
@ r10: address of current particle position
|
||||
@ r11: address of comparator particle positions
|
||||
@ r12: isClose (compacted)
|
||||
@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
@
|
||||
@ Scratch
|
||||
@ q0 == index ------> packedIndices
|
||||
@ q1 == positionX ---_ distBtParticlesSq
|
||||
@ q2 == positionY --_ --> normX
|
||||
@ q3 == ---> normY
|
||||
@
|
||||
@ Unused (note: these are callee-saved)
|
||||
@ q4 ==
|
||||
@ q5 ==
|
||||
@ q6 ==
|
||||
@ q7 ==
|
||||
@
|
||||
@ Scratch
|
||||
@ q8 == comparatorIndices
|
||||
@ q9 == comparatorPositionX
|
||||
@ q10 == comparatorPositionY
|
||||
@ q11 ==
|
||||
@
|
||||
@ Constants
|
||||
@ q12 == 1.0f
|
||||
@ q13 == isClose table indices
|
||||
@ q14 == 1 / particleDiameter
|
||||
@ q15 == particleDiameterSq
|
||||
|
||||
push {r4-r11, lr}
|
||||
|
||||
@ Load constants from registers and stack.
|
||||
vld1.f32 {d30[],d31[]}, [r3] @ q15 = particleDiameterSq
|
||||
ldr r12, [sp, #36] @ r12 = particleDiameterInv
|
||||
vld1.f32 {d28[],d29[]}, [r12] @ q14 = particleDiameterInv
|
||||
ldr r9, [sp, #44] @ r9 = contacts
|
||||
ldr r7, [sp, #40] @ r7 = flags
|
||||
ldr r3, [r9, #0] @ r3 = out = contacts.data
|
||||
ldr r6, [r9, #8] @ r6 = contacts.capacity
|
||||
mov r4, r3 @ r4 = postProcess = outContacts
|
||||
mov r5, #0 @ r5 = numContacts
|
||||
sub r6, r6, #8 @ r6 = maxSafeContacts = capacity - 8
|
||||
mov r8, #12 @ r8 = 12 = sizeof(FindContactInput)
|
||||
|
||||
@ Perform zero iterations if 'numChecks' is empty.
|
||||
@ Must happen after initializing r5 = numContacts = 0.
|
||||
cmp r2, #0
|
||||
ble .L_FindContacts_Return
|
||||
|
||||
@ Load and calculate remaining constants.
|
||||
vmov.f32 q12, #1.0 @ q12 = 1.0f splatted
|
||||
adr r12, CONST_IS_CLOSE_TABLE_INDICES
|
||||
vld1.8 {d26}, [r12] @ q13 = *CONST_IS_CLOSE_TABLE_INDICES
|
||||
|
||||
.L_FindContacts_MainLoop:
|
||||
pld [r1, #8] @ prefetch two loops ahead
|
||||
|
||||
@ r10 <== Address of 'position', the current particle position
|
||||
@ r11 <== Address of '&comparator[0]', the first particle position we
|
||||
@ compare against.
|
||||
ldr r10, [r1], #4 @ r10 = positionIndex|comparatorIndex
|
||||
smlatb r11, r10, r8, r0 @ r11 = address of first comparator
|
||||
smlabb r10, r10, r8, r0 @ r10 = address of current input
|
||||
add r12, r11, #24 @ r12 = address of third comparator
|
||||
|
||||
@ Exit if not enough space in output array (part 1)
|
||||
cmp r5, r6
|
||||
|
||||
@ {q0, q1, q2} == index, positionX, positionY, splatted across vector
|
||||
vld3.f32 {d0[], d2[], d4[]}, [r10]
|
||||
vld3.f32 {d1[], d3[], d5[]}, [r10]
|
||||
|
||||
@ {q8, q9, q10} == comparatorIndices, comparatorPosX and comparatorPosY
|
||||
@ positions we compare against (positionX, positionY)
|
||||
vld3.f32 {d16, d18, d20}, [r11]
|
||||
vld3.f32 {d17, d19, d21}, [r12]
|
||||
|
||||
@ q0 = packedIndices -- indices output to b2ParticleContact
|
||||
@ q1 = distBtParticlesSq -- will be used to calculate weight
|
||||
@ q2 = diffX -- will be used to calculate normal
|
||||
@ q3 = diffY -- will be used to calculate normal
|
||||
vsub.f32 q3, q10, q2 @ q3 = diffY = comparatorPosY - positionY
|
||||
vsub.f32 q2, q9, q1 @ q2 = diffX = comparatorPosX - positionX
|
||||
vsli.32 q0, q8, #16 @ q0 = comparatorIndex[i] << 16 | index
|
||||
vmul.f32 q1, q3, q3 @ q1 = diffX * diffX
|
||||
vmla.f32 q1, q2, q2 @ q1 = diffX * diffX + diffY * diffY
|
||||
|
||||
@ Determine if each particle is close enough to output.
|
||||
@ Pack the isClose bitmap (four T or F) into a 32-bit bitmap.
|
||||
@ Move 32-bit bitmap to CPU register, for conditional operations.
|
||||
@ Note: NEON to CPU register moves are slow (20 cyclds) on some
|
||||
@ implementations of NEON.
|
||||
@ isClose = distBtParticlesSq < particleDiameterSq
|
||||
vclt.f32 q8, q1, q15 @ q8 == isClose
|
||||
vtbl.8 d16, {d16,d17}, d26 @ q8[0] == isClose(packed)
|
||||
vmov.32 r12, d16[0] @ q8[0] ==> r12.
|
||||
|
||||
@ If not enough space in output array, grow it.
|
||||
@ This is a heavy operation, but should happen rarely.
|
||||
ble .L_FindContacts_Output
|
||||
ldr r9, [sp, #44] @ r9 = contacts
|
||||
str r5, [r9, #4] @ contacts.count = numContacts
|
||||
ldr r10, [r9, #0] @ r10 = contacts.data
|
||||
push {r0-r3, r9, r10, r12}
|
||||
vpush {q0, q1, q2, q3}
|
||||
vpush {q12, q13, q14, q15}
|
||||
mov r0, r9 @ r0 = contacts
|
||||
bl GrowParticleContactBuffer
|
||||
vpop {q12, q13, q14, q15}
|
||||
vpop {q0, q1, q2, q3}
|
||||
pop {r0-r3, r9, r10, r12}
|
||||
|
||||
@ The output array was reallocated, so update 'out', 'postProcess' and
|
||||
@ 'maxSafeContacts' pointers.
|
||||
ldr r6, [r9, #8] @ r6 = contacts.capacity
|
||||
ldr r9, [r9, #0] @ r9 = contacts.data
|
||||
sub r9, r9, r10 @ r9 = data buffer offset
|
||||
sub r6, r6, #8 @ r6 = maxSafeContacts
|
||||
add r3, r3, r9 @ r3 += data buffer offset
|
||||
add r4, r4, r9 @ r4 += data buffer offset
|
||||
|
||||
.L_FindContacts_Output:
|
||||
@ Store results to memory, but only results that are close
|
||||
tst r12, 0xFF
|
||||
it ne
|
||||
vst4ne.32 {d0[0],d2[0],d4[0],d6[0]}, [r3]! @ Store 1st contact
|
||||
|
||||
tst r12, 0xFF00
|
||||
it ne
|
||||
vst4ne.32 {d0[1],d2[1],d4[1],d6[1]}, [r3]! @ Store 2nd contact
|
||||
|
||||
tst r12, 0xFF0000
|
||||
it ne
|
||||
vst4ne.32 {d1[0],d3[0],d5[0],d7[0]}, [r3]! @ Store 3rd contact
|
||||
|
||||
tst r12, 0xFF000000
|
||||
it ne
|
||||
vst4ne.32 {d1[1],d3[1],d5[1],d7[1]}, [r3]! @ Store 4th contact
|
||||
|
||||
@ post-process the last four elements that have been output
|
||||
@ r12 = 5th element to not be post-processed yet
|
||||
add r12, r4, #64 @ r12 = nextPostProcess
|
||||
cmp r3, r12
|
||||
it ge
|
||||
blge FindContacts_PostProcess
|
||||
|
||||
@ decrement loop counter; sets the 'gt' flag used in 'bgt' below
|
||||
subs r2, r2, #1
|
||||
bgt .L_FindContacts_MainLoop
|
||||
|
||||
.L_FindContacts_PostProcessRemainingItems:
|
||||
@ If at least one output item needs post-processing, do it.
|
||||
subs r12, r3, r4
|
||||
ble .L_FindContacts_Return
|
||||
|
||||
@ r12/16 = num extra contacts to process
|
||||
add r5, r5, r12, lsr #4 @ numContacts += num extra
|
||||
push {r5} @ Save numContacts, since stomped
|
||||
|
||||
@ Ensure indices past end of array are zeroed out.
|
||||
@ We process 4 contacts in FindContacts_PostProcess, even if we only
|
||||
@ have one left to process.
|
||||
mov r12, #0
|
||||
str r12, [r3]
|
||||
str r12, [r3, #16]
|
||||
str r12, [r3, #32]
|
||||
|
||||
bl FindContacts_PostProcess
|
||||
pop {r5} @ Restore numContacts
|
||||
|
||||
.L_FindContacts_Return:
|
||||
@ Set the final number of contacts in the output buffer.
|
||||
ldr r9, [sp, #44] @ r9 = contacts
|
||||
str r5, [r9, #4] @ contacts.count = numContacts
|
||||
|
||||
@ Return by popping the original lr into pc.
|
||||
pop {r4-r11, pc}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Google, Inc.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include <Box2D/Particle/b2ParticleGroup.h>
|
||||
#include <Box2D/Particle/b2ParticleSystem.h>
|
||||
#include <Box2D/Dynamics/b2World.h>
|
||||
|
||||
#if LIQUIDFUN_EXTERNAL_LANGUAGE_API
|
||||
#include <Box2D/Collision/Shapes/b2CircleShape.h>
|
||||
#endif //LIQUIDFUN_EXTERNAL_LANGUAGE_API
|
||||
|
||||
b2ParticleGroup::b2ParticleGroup()
|
||||
{
|
||||
|
||||
m_system = NULL;
|
||||
m_firstIndex = 0;
|
||||
m_lastIndex = 0;
|
||||
m_groupFlags = 0;
|
||||
m_strength = 1.0f;
|
||||
m_prev = NULL;
|
||||
m_next = NULL;
|
||||
|
||||
m_timestamp = -1;
|
||||
m_mass = 0;
|
||||
m_inertia = 0;
|
||||
m_center = b2Vec2_zero;
|
||||
m_linearVelocity = b2Vec2_zero;
|
||||
m_angularVelocity = 0;
|
||||
m_transform.SetIdentity();
|
||||
|
||||
m_userData = NULL;
|
||||
|
||||
}
|
||||
|
||||
uint32 b2ParticleGroup::GetAllParticleFlags() const
|
||||
{
|
||||
uint32 flags = 0;
|
||||
for (int32 i = m_firstIndex; i < m_lastIndex; i++)
|
||||
{
|
||||
flags |= m_system->m_flagsBuffer.data[i];
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
void b2ParticleGroup::SetGroupFlags(uint32 flags)
|
||||
{
|
||||
b2Assert((flags & b2_particleGroupInternalMask) == 0);
|
||||
flags |= m_groupFlags & b2_particleGroupInternalMask;
|
||||
m_system->SetGroupFlags(this, flags);
|
||||
}
|
||||
|
||||
void b2ParticleGroup::UpdateStatistics() const
|
||||
{
|
||||
if (m_timestamp != m_system->m_timestamp)
|
||||
{
|
||||
float32 m = m_system->GetParticleMass();
|
||||
m_mass = 0;
|
||||
m_center.SetZero();
|
||||
m_linearVelocity.SetZero();
|
||||
for (int32 i = m_firstIndex; i < m_lastIndex; i++)
|
||||
{
|
||||
m_mass += m;
|
||||
m_center += m * m_system->m_positionBuffer.data[i];
|
||||
m_linearVelocity += m * m_system->m_velocityBuffer.data[i];
|
||||
}
|
||||
if (m_mass > 0)
|
||||
{
|
||||
m_center *= 1 / m_mass;
|
||||
m_linearVelocity *= 1 / m_mass;
|
||||
}
|
||||
m_inertia = 0;
|
||||
m_angularVelocity = 0;
|
||||
for (int32 i = m_firstIndex; i < m_lastIndex; i++)
|
||||
{
|
||||
b2Vec2 p = m_system->m_positionBuffer.data[i] - m_center;
|
||||
b2Vec2 v = m_system->m_velocityBuffer.data[i] - m_linearVelocity;
|
||||
m_inertia += m * b2Dot(p, p);
|
||||
m_angularVelocity += m * b2Cross(p, v);
|
||||
}
|
||||
if (m_inertia > 0)
|
||||
{
|
||||
m_angularVelocity *= 1 / m_inertia;
|
||||
}
|
||||
m_timestamp = m_system->m_timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
void b2ParticleGroup::ApplyForce(const b2Vec2& force)
|
||||
{
|
||||
m_system->ApplyForce(m_firstIndex, m_lastIndex, force);
|
||||
}
|
||||
|
||||
void b2ParticleGroup::ApplyLinearImpulse(const b2Vec2& impulse)
|
||||
{
|
||||
m_system->ApplyLinearImpulse(m_firstIndex, m_lastIndex, impulse);
|
||||
}
|
||||
|
||||
void b2ParticleGroup::DestroyParticles(bool callDestructionListener)
|
||||
{
|
||||
b2Assert(m_system->m_world->IsLocked() == false);
|
||||
if (m_system->m_world->IsLocked())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int32 i = m_firstIndex; i < m_lastIndex; i++) {
|
||||
m_system->DestroyParticle(i, callDestructionListener);
|
||||
}
|
||||
}
|
||||
|
||||
#if LIQUIDFUN_EXTERNAL_LANGUAGE_API
|
||||
void b2ParticleGroupDef::FreeShapesMemory() {
|
||||
if (circleShapes)
|
||||
{
|
||||
delete[] circleShapes;
|
||||
circleShapes = NULL;
|
||||
}
|
||||
if (ownShapesArray && shapes)
|
||||
{
|
||||
delete[] shapes;
|
||||
shapes = NULL;
|
||||
ownShapesArray = false;
|
||||
}
|
||||
}
|
||||
|
||||
void b2ParticleGroupDef::SetCircleShapesFromVertexList(void* inBuf,
|
||||
int numShapes,
|
||||
float radius)
|
||||
{
|
||||
float* points = (float*) inBuf;
|
||||
// Create circle shapes from vertex list and radius
|
||||
b2CircleShape* pCircleShapes = new b2CircleShape[numShapes];
|
||||
b2Shape** pShapes = new b2Shape*[numShapes];
|
||||
for (int i = 0; i < numShapes; ++i) {
|
||||
pCircleShapes[i].m_radius = radius;
|
||||
pCircleShapes[i].m_p = b2Vec2(points[i*2], points[i*2+1]);
|
||||
pShapes[i] = &pCircleShapes[i];
|
||||
}
|
||||
|
||||
// Clean up existing buffers
|
||||
FreeShapesMemory();
|
||||
|
||||
// Assign to newly created buffers
|
||||
ownShapesArray = true;
|
||||
circleShapes = pCircleShapes;
|
||||
shapes = pShapes;
|
||||
shapeCount = numShapes;
|
||||
}
|
||||
#endif // LIQUIDFUN_EXTERNAL_LANGUAGE_API
|
||||
+413
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Google, Inc.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#ifndef B2_PARTICLE_GROUP
|
||||
#define B2_PARTICLE_GROUP
|
||||
|
||||
#include <Box2D/Particle/b2Particle.h>
|
||||
|
||||
class b2Shape;
|
||||
class b2World;
|
||||
class b2ParticleSystem;
|
||||
class b2ParticleGroup;
|
||||
class b2ParticleColor;
|
||||
#if LIQUIDFUN_EXTERNAL_LANGUAGE_API
|
||||
class b2CircleShape;
|
||||
#endif // LIQUIDFUN_EXTERNAL_LANGUAGE_API
|
||||
|
||||
/// @file
|
||||
|
||||
/// The particle group type. Can be combined with the | operator.
|
||||
enum b2ParticleGroupFlag
|
||||
{
|
||||
/// Prevents overlapping or leaking.
|
||||
b2_solidParticleGroup = 1 << 0,
|
||||
/// Keeps its shape.
|
||||
b2_rigidParticleGroup = 1 << 1,
|
||||
/// Won't be destroyed if it gets empty.
|
||||
b2_particleGroupCanBeEmpty = 1 << 2,
|
||||
/// Will be destroyed on next simulation step.
|
||||
b2_particleGroupWillBeDestroyed = 1 << 3,
|
||||
/// Updates depth data on next simulation step.
|
||||
b2_particleGroupNeedsUpdateDepth = 1 << 4,
|
||||
b2_particleGroupInternalMask =
|
||||
b2_particleGroupWillBeDestroyed |
|
||||
b2_particleGroupNeedsUpdateDepth,
|
||||
};
|
||||
|
||||
/// A particle group definition holds all the data needed to construct a
|
||||
/// particle group. You can safely re-use these definitions.
|
||||
struct b2ParticleGroupDef
|
||||
{
|
||||
|
||||
b2ParticleGroupDef()
|
||||
{
|
||||
flags = 0;
|
||||
groupFlags = 0;
|
||||
position = b2Vec2_zero;
|
||||
angle = 0;
|
||||
linearVelocity = b2Vec2_zero;
|
||||
angularVelocity = 0;
|
||||
color = b2ParticleColor_zero;
|
||||
strength = 1;
|
||||
shape = NULL;
|
||||
shapes = NULL;
|
||||
shapeCount = 0;
|
||||
stride = 0;
|
||||
particleCount = 0;
|
||||
positionData = NULL;
|
||||
lifetime = 0.0f;
|
||||
userData = NULL;
|
||||
group = NULL;
|
||||
|
||||
#if LIQUIDFUN_EXTERNAL_LANGUAGE_API
|
||||
circleShapes = NULL;
|
||||
ownShapesArray = false;
|
||||
#endif // LIQUIDFUN_EXTERNAL_LANGUAGE_API
|
||||
}
|
||||
|
||||
~b2ParticleGroupDef()
|
||||
{
|
||||
#if LIQUIDFUN_EXTERNAL_LANGUAGE_API
|
||||
FreeShapesMemory();
|
||||
#endif // LIQUIDFUN_EXTERNAL_LANGUAGE_API
|
||||
}
|
||||
|
||||
/// The particle-behavior flags (See #b2ParticleFlag).
|
||||
uint32 flags;
|
||||
|
||||
/// The group-construction flags (See #b2ParticleGroupFlag).
|
||||
uint32 groupFlags;
|
||||
|
||||
/// The world position of the group.
|
||||
/// Moves the group's shape a distance equal to the value of position.
|
||||
b2Vec2 position;
|
||||
|
||||
/// The world angle of the group in radians.
|
||||
/// Rotates the shape by an angle equal to the value of angle.
|
||||
float32 angle;
|
||||
|
||||
/// The linear velocity of the group's origin in world co-ordinates.
|
||||
b2Vec2 linearVelocity;
|
||||
|
||||
/// The angular velocity of the group.
|
||||
float32 angularVelocity;
|
||||
|
||||
/// The color of all particles in the group.
|
||||
b2ParticleColor color;
|
||||
|
||||
/// The strength of cohesion among the particles in a group with flag
|
||||
/// b2_elasticParticle or b2_springParticle.
|
||||
float32 strength;
|
||||
|
||||
/// The shape where particles will be added.
|
||||
const b2Shape* shape;
|
||||
|
||||
/// A array of shapes where particles will be added.
|
||||
const b2Shape* const* shapes;
|
||||
|
||||
/// The number of shapes.
|
||||
int32 shapeCount;
|
||||
|
||||
/// The interval of particles in the shape.
|
||||
/// If it is 0, b2_particleStride * particleDiameter is used instead.
|
||||
float32 stride;
|
||||
|
||||
/// The number of particles in addition to ones added in the shape.
|
||||
int32 particleCount;
|
||||
|
||||
/// The initial positions of the particleCount particles.
|
||||
const b2Vec2* positionData;
|
||||
|
||||
/// Lifetime of the particle group in seconds. A value <= 0.0f indicates a
|
||||
/// particle group with infinite lifetime.
|
||||
float32 lifetime;
|
||||
|
||||
/// Use this to store application-specific group data.
|
||||
void* userData;
|
||||
|
||||
/// An existing particle group to which the particles will be added.
|
||||
b2ParticleGroup* group;
|
||||
|
||||
#if LIQUIDFUN_EXTERNAL_LANGUAGE_API
|
||||
/// Storage for constructed CircleShapes from an incoming vertex list
|
||||
const b2CircleShape* circleShapes;
|
||||
|
||||
/// True if we create the shapes array internally.
|
||||
bool ownShapesArray;
|
||||
|
||||
/// Clean up all memory associated with SetCircleShapesFromVertexList
|
||||
void FreeShapesMemory();
|
||||
|
||||
/// From a vertex list created by an external language API, construct
|
||||
/// a list of circle shapes that can be used to create a b2ParticleGroup
|
||||
/// This eliminates cumbersome array-interfaces between languages.
|
||||
void SetCircleShapesFromVertexList(void* inBuf,
|
||||
int numShapes,
|
||||
float radius);
|
||||
|
||||
/// Set position with direct floats.
|
||||
void SetPosition(float32 x, float32 y);
|
||||
|
||||
/// Set color with direct ints.
|
||||
void SetColor(int32 r, int32 g, int32 b, int32 a);
|
||||
#endif // LIQUIDFUN_EXTERNAL_LANGUAGE_API
|
||||
};
|
||||
|
||||
/// A group of particles. b2ParticleGroup::CreateParticleGroup creates these.
|
||||
class b2ParticleGroup
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
/// Get the next particle group from the list in b2_World.
|
||||
b2ParticleGroup* GetNext();
|
||||
const b2ParticleGroup* GetNext() const;
|
||||
|
||||
/// Get the particle system that holds this particle group.
|
||||
b2ParticleSystem* GetParticleSystem();
|
||||
const b2ParticleSystem* GetParticleSystem() const;
|
||||
|
||||
/// Get the number of particles.
|
||||
int32 GetParticleCount() const;
|
||||
|
||||
/// Get the offset of this group in the global particle buffer
|
||||
int32 GetBufferIndex() const;
|
||||
|
||||
/// Does this group contain the particle.
|
||||
bool ContainsParticle(int32 index) const;
|
||||
|
||||
/// Get the logical sum of particle flags.
|
||||
uint32 GetAllParticleFlags() const;
|
||||
|
||||
/// Get the construction flags for the group.
|
||||
uint32 GetGroupFlags() const;
|
||||
|
||||
/// Set the construction flags for the group.
|
||||
void SetGroupFlags(uint32 flags);
|
||||
|
||||
/// Get the total mass of the group: the sum of all particles in it.
|
||||
float32 GetMass() const;
|
||||
|
||||
/// Get the moment of inertia for the group.
|
||||
float32 GetInertia() const;
|
||||
|
||||
/// Get the center of gravity for the group.
|
||||
b2Vec2 GetCenter() const;
|
||||
|
||||
/// Get the linear velocity of the group.
|
||||
b2Vec2 GetLinearVelocity() const;
|
||||
|
||||
/// Get the angular velocity of the group.
|
||||
float32 GetAngularVelocity() const;
|
||||
|
||||
/// Get the position of the group's origin and rotation.
|
||||
/// Used only with groups of rigid particles.
|
||||
const b2Transform& GetTransform() const;
|
||||
|
||||
/// Get position of the particle group as a whole.
|
||||
/// Used only with groups of rigid particles.
|
||||
const b2Vec2& GetPosition() const;
|
||||
|
||||
/// Get the rotational angle of the particle group as a whole.
|
||||
/// Used only with groups of rigid particles.
|
||||
float32 GetAngle() const;
|
||||
|
||||
/// Get the world linear velocity of a world point, from the average linear
|
||||
/// and angular velocities of the particle group.
|
||||
/// @param a point in world coordinates.
|
||||
/// @return the world velocity of a point.
|
||||
b2Vec2 GetLinearVelocityFromWorldPoint(const b2Vec2& worldPoint) const;
|
||||
|
||||
/// Get the user data pointer that was provided in the group definition.
|
||||
void* GetUserData() const;
|
||||
|
||||
/// Set the user data. Use this to store your application specific data.
|
||||
void SetUserData(void* data);
|
||||
|
||||
/// Call b2ParticleSystem::ApplyForce for every particle in the group.
|
||||
void ApplyForce(const b2Vec2& force);
|
||||
|
||||
/// Call b2ParticleSystem::ApplyLinearImpulse for every particle in the
|
||||
/// group.
|
||||
void ApplyLinearImpulse(const b2Vec2& impulse);
|
||||
|
||||
/// Destroy all the particles in this group.
|
||||
/// This function is locked during callbacks.
|
||||
/// @param Whether to call the world b2DestructionListener for each
|
||||
/// particle is destroyed.
|
||||
/// @warning This function is locked during callbacks.
|
||||
void DestroyParticles(bool callDestructionListener);
|
||||
|
||||
/// Destroy all particles in this group without enabling the destruction
|
||||
/// callback for destroyed particles.
|
||||
/// This function is locked during callbacks.
|
||||
/// @warning This function is locked during callbacks.
|
||||
void DestroyParticles();
|
||||
|
||||
private:
|
||||
|
||||
friend class b2ParticleSystem;
|
||||
|
||||
b2ParticleSystem* m_system;
|
||||
int32 m_firstIndex, m_lastIndex;
|
||||
uint32 m_groupFlags;
|
||||
float32 m_strength;
|
||||
b2ParticleGroup* m_prev;
|
||||
b2ParticleGroup* m_next;
|
||||
|
||||
mutable int32 m_timestamp;
|
||||
mutable float32 m_mass;
|
||||
mutable float32 m_inertia;
|
||||
mutable b2Vec2 m_center;
|
||||
mutable b2Vec2 m_linearVelocity;
|
||||
mutable float32 m_angularVelocity;
|
||||
mutable b2Transform m_transform;
|
||||
|
||||
void* m_userData;
|
||||
|
||||
b2ParticleGroup();
|
||||
~b2ParticleGroup();
|
||||
void UpdateStatistics() const;
|
||||
|
||||
};
|
||||
|
||||
inline b2ParticleGroup* b2ParticleGroup::GetNext()
|
||||
{
|
||||
return m_next;
|
||||
}
|
||||
|
||||
inline const b2ParticleGroup* b2ParticleGroup::GetNext() const
|
||||
{
|
||||
return m_next;
|
||||
}
|
||||
|
||||
inline b2ParticleSystem* b2ParticleGroup::GetParticleSystem()
|
||||
{
|
||||
return m_system;
|
||||
}
|
||||
|
||||
inline const b2ParticleSystem* b2ParticleGroup::GetParticleSystem() const
|
||||
{
|
||||
return m_system;
|
||||
}
|
||||
|
||||
inline int32 b2ParticleGroup::GetParticleCount() const
|
||||
{
|
||||
return m_lastIndex - m_firstIndex;
|
||||
}
|
||||
|
||||
inline bool b2ParticleGroup::ContainsParticle(int32 index) const
|
||||
{
|
||||
return m_firstIndex <= index && index < m_lastIndex;
|
||||
}
|
||||
|
||||
inline b2ParticleGroup::~b2ParticleGroup()
|
||||
{
|
||||
}
|
||||
|
||||
inline int32 b2ParticleGroup::GetBufferIndex() const
|
||||
{
|
||||
return m_firstIndex;
|
||||
}
|
||||
|
||||
inline uint32 b2ParticleGroup::GetGroupFlags() const
|
||||
{
|
||||
return m_groupFlags & ~b2_particleGroupInternalMask;
|
||||
}
|
||||
|
||||
inline float32 b2ParticleGroup::GetMass() const
|
||||
{
|
||||
UpdateStatistics();
|
||||
return m_mass;
|
||||
}
|
||||
|
||||
inline float32 b2ParticleGroup::GetInertia() const
|
||||
{
|
||||
UpdateStatistics();
|
||||
return m_inertia;
|
||||
}
|
||||
|
||||
inline b2Vec2 b2ParticleGroup::GetCenter() const
|
||||
{
|
||||
UpdateStatistics();
|
||||
return m_center;
|
||||
}
|
||||
|
||||
inline b2Vec2 b2ParticleGroup::GetLinearVelocity() const
|
||||
{
|
||||
UpdateStatistics();
|
||||
return m_linearVelocity;
|
||||
}
|
||||
|
||||
inline float32 b2ParticleGroup::GetAngularVelocity() const
|
||||
{
|
||||
UpdateStatistics();
|
||||
return m_angularVelocity;
|
||||
}
|
||||
|
||||
inline const b2Transform& b2ParticleGroup::GetTransform() const
|
||||
{
|
||||
return m_transform;
|
||||
}
|
||||
|
||||
inline const b2Vec2& b2ParticleGroup::GetPosition() const
|
||||
{
|
||||
return m_transform.p;
|
||||
}
|
||||
|
||||
inline float32 b2ParticleGroup::GetAngle() const
|
||||
{
|
||||
return m_transform.q.GetAngle();
|
||||
}
|
||||
|
||||
inline b2Vec2 b2ParticleGroup::GetLinearVelocityFromWorldPoint(
|
||||
const b2Vec2& worldPoint) const
|
||||
{
|
||||
UpdateStatistics();
|
||||
return m_linearVelocity + b2Cross(m_angularVelocity, worldPoint - m_center);
|
||||
}
|
||||
|
||||
inline void* b2ParticleGroup::GetUserData() const
|
||||
{
|
||||
return m_userData;
|
||||
}
|
||||
|
||||
inline void b2ParticleGroup::SetUserData(void* data)
|
||||
{
|
||||
m_userData = data;
|
||||
}
|
||||
|
||||
inline void b2ParticleGroup::DestroyParticles()
|
||||
{
|
||||
DestroyParticles(false);
|
||||
}
|
||||
|
||||
#if LIQUIDFUN_EXTERNAL_LANGUAGE_API
|
||||
inline void b2ParticleGroupDef::SetPosition(float32 x, float32 y)
|
||||
{
|
||||
position.Set(x, y);
|
||||
}
|
||||
|
||||
inline void b2ParticleGroupDef::SetColor(int32 r, int32 g, int32 b, int32 a)
|
||||
{
|
||||
color.Set((uint8)r, (uint8)g, (uint8)b, (uint8)a);
|
||||
}
|
||||
#endif // LIQUIDFUN_EXTERNAL_LANGUAGE_API
|
||||
|
||||
|
||||
#endif
|
||||
+4670
File diff suppressed because it is too large
Load Diff
+1544
File diff suppressed because it is too large
Load Diff
+98
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Google, Inc.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#ifndef B2_STACK_QUEUE
|
||||
#define B2_STACK_QUEUE
|
||||
|
||||
#include <Box2D/Common/b2StackAllocator.h>
|
||||
|
||||
template <typename T>
|
||||
class b2StackQueue
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
b2StackQueue(b2StackAllocator *allocator, int32 capacity)
|
||||
{
|
||||
m_allocator = allocator;
|
||||
m_buffer = (T*) m_allocator->Allocate(sizeof(T) * capacity);
|
||||
m_front = 0;
|
||||
m_back = 0;
|
||||
m_capacity = capacity;
|
||||
}
|
||||
|
||||
~b2StackQueue()
|
||||
{
|
||||
m_allocator->Free(m_buffer);
|
||||
}
|
||||
|
||||
void Push(const T &item)
|
||||
{
|
||||
if (m_back >= m_capacity)
|
||||
{
|
||||
for (int32 i = m_front; i < m_back; i++)
|
||||
{
|
||||
m_buffer[i - m_front] = m_buffer[i];
|
||||
}
|
||||
m_back -= m_front;
|
||||
m_front = 0;
|
||||
if (m_back >= m_capacity)
|
||||
{
|
||||
if (m_capacity > 0)
|
||||
{
|
||||
m_capacity *= 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_capacity = 1;
|
||||
}
|
||||
m_buffer = (T*) m_allocator->Reallocate(m_buffer,
|
||||
sizeof(T) * m_capacity);
|
||||
}
|
||||
}
|
||||
m_buffer[m_back] = item;
|
||||
m_back++;
|
||||
}
|
||||
|
||||
void Pop()
|
||||
{
|
||||
b2Assert(m_front < m_back);
|
||||
m_front++;
|
||||
}
|
||||
|
||||
bool Empty() const
|
||||
{
|
||||
b2Assert(m_front <= m_back);
|
||||
return m_front == m_back;
|
||||
}
|
||||
|
||||
const T &Front() const
|
||||
{
|
||||
return m_buffer[m_front];
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
b2StackAllocator *m_allocator;
|
||||
T* m_buffer;
|
||||
int32 m_front;
|
||||
int32 m_back;
|
||||
int32 m_capacity;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Google, Inc.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include <Box2D/Particle/b2VoronoiDiagram.h>
|
||||
#include <Box2D/Particle/b2StackQueue.h>
|
||||
#include <Box2D/Collision/b2Collision.h>
|
||||
|
||||
b2VoronoiDiagram::b2VoronoiDiagram(
|
||||
b2StackAllocator* allocator, int32 generatorCapacity)
|
||||
{
|
||||
m_allocator = allocator;
|
||||
m_generatorBuffer =
|
||||
(Generator*) allocator->Allocate(
|
||||
sizeof(Generator) * generatorCapacity);
|
||||
m_generatorCapacity = generatorCapacity;
|
||||
m_generatorCount = 0;
|
||||
m_countX = 0;
|
||||
m_countY = 0;
|
||||
m_diagram = NULL;
|
||||
}
|
||||
|
||||
b2VoronoiDiagram::~b2VoronoiDiagram()
|
||||
{
|
||||
if (m_diagram)
|
||||
{
|
||||
m_allocator->Free(m_diagram);
|
||||
}
|
||||
m_allocator->Free(m_generatorBuffer);
|
||||
}
|
||||
|
||||
void b2VoronoiDiagram::AddGenerator(
|
||||
const b2Vec2& center, int32 tag, bool necessary)
|
||||
{
|
||||
b2Assert(m_generatorCount < m_generatorCapacity);
|
||||
Generator& g = m_generatorBuffer[m_generatorCount++];
|
||||
g.center = center;
|
||||
g.tag = tag;
|
||||
g.necessary = necessary;
|
||||
}
|
||||
|
||||
void b2VoronoiDiagram::Generate(float32 radius, float32 margin)
|
||||
{
|
||||
b2Assert(m_diagram == NULL);
|
||||
float32 inverseRadius = 1 / radius;
|
||||
b2Vec2 lower(+b2_maxFloat, +b2_maxFloat);
|
||||
b2Vec2 upper(-b2_maxFloat, -b2_maxFloat);
|
||||
for (int32 k = 0; k < m_generatorCount; k++)
|
||||
{
|
||||
Generator& g = m_generatorBuffer[k];
|
||||
if (g.necessary)
|
||||
{
|
||||
lower = b2Min(lower, g.center);
|
||||
upper = b2Max(upper, g.center);
|
||||
}
|
||||
}
|
||||
lower.x -= margin;
|
||||
lower.y -= margin;
|
||||
upper.x += margin;
|
||||
upper.y += margin;
|
||||
m_countX = 1 + (int32) (inverseRadius * (upper.x - lower.x));
|
||||
m_countY = 1 + (int32) (inverseRadius * (upper.y - lower.y));
|
||||
m_diagram = (Generator**)
|
||||
m_allocator->Allocate(sizeof(Generator*) * m_countX * m_countY);
|
||||
for (int32 i = 0; i < m_countX * m_countY; i++)
|
||||
{
|
||||
m_diagram[i] = NULL;
|
||||
}
|
||||
// (4 * m_countX * m_countY) is the queue capacity that is experimentally
|
||||
// known to be necessary and sufficient for general particle distributions.
|
||||
b2StackQueue<b2VoronoiDiagramTask> queue(
|
||||
m_allocator, 4 * m_countX * m_countY);
|
||||
for (int32 k = 0; k < m_generatorCount; k++)
|
||||
{
|
||||
Generator& g = m_generatorBuffer[k];
|
||||
g.center = inverseRadius * (g.center - lower);
|
||||
int32 x = (int32) g.center.x;
|
||||
int32 y = (int32) g.center.y;
|
||||
if (x >=0 && y >= 0 && x < m_countX && y < m_countY)
|
||||
{
|
||||
queue.Push(b2VoronoiDiagramTask(x, y, x + y * m_countX, &g));
|
||||
}
|
||||
}
|
||||
while (!queue.Empty())
|
||||
{
|
||||
int32 x = queue.Front().m_x;
|
||||
int32 y = queue.Front().m_y;
|
||||
int32 i = queue.Front().m_i;
|
||||
Generator* g = queue.Front().m_generator;
|
||||
queue.Pop();
|
||||
if (!m_diagram[i])
|
||||
{
|
||||
m_diagram[i] = g;
|
||||
if (x > 0)
|
||||
{
|
||||
queue.Push(b2VoronoiDiagramTask(x - 1, y, i - 1, g));
|
||||
}
|
||||
if (y > 0)
|
||||
{
|
||||
queue.Push(b2VoronoiDiagramTask(x, y - 1, i - m_countX, g));
|
||||
}
|
||||
if (x < m_countX - 1)
|
||||
{
|
||||
queue.Push(b2VoronoiDiagramTask(x + 1, y, i + 1, g));
|
||||
}
|
||||
if (y < m_countY - 1)
|
||||
{
|
||||
queue.Push(b2VoronoiDiagramTask(x, y + 1, i + m_countX, g));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int32 y = 0; y < m_countY; y++)
|
||||
{
|
||||
for (int32 x = 0; x < m_countX - 1; x++)
|
||||
{
|
||||
int32 i = x + y * m_countX;
|
||||
Generator* a = m_diagram[i];
|
||||
Generator* b = m_diagram[i + 1];
|
||||
if (a != b)
|
||||
{
|
||||
queue.Push(b2VoronoiDiagramTask(x, y, i, b));
|
||||
queue.Push(b2VoronoiDiagramTask(x + 1, y, i + 1, a));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int32 y = 0; y < m_countY - 1; y++)
|
||||
{
|
||||
for (int32 x = 0; x < m_countX; x++)
|
||||
{
|
||||
int32 i = x + y * m_countX;
|
||||
Generator* a = m_diagram[i];
|
||||
Generator* b = m_diagram[i + m_countX];
|
||||
if (a != b)
|
||||
{
|
||||
queue.Push(b2VoronoiDiagramTask(x, y, i, b));
|
||||
queue.Push(b2VoronoiDiagramTask(x, y + 1, i + m_countX, a));
|
||||
}
|
||||
}
|
||||
}
|
||||
while (!queue.Empty())
|
||||
{
|
||||
const b2VoronoiDiagramTask& task = queue.Front();
|
||||
int32 x = task.m_x;
|
||||
int32 y = task.m_y;
|
||||
int32 i = task.m_i;
|
||||
Generator* k = task.m_generator;
|
||||
queue.Pop();
|
||||
Generator* a = m_diagram[i];
|
||||
Generator* b = k;
|
||||
if (a != b)
|
||||
{
|
||||
float32 ax = a->center.x - x;
|
||||
float32 ay = a->center.y - y;
|
||||
float32 bx = b->center.x - x;
|
||||
float32 by = b->center.y - y;
|
||||
float32 a2 = ax * ax + ay * ay;
|
||||
float32 b2 = bx * bx + by * by;
|
||||
if (a2 > b2)
|
||||
{
|
||||
m_diagram[i] = b;
|
||||
if (x > 0)
|
||||
{
|
||||
queue.Push(b2VoronoiDiagramTask(x - 1, y, i - 1, b));
|
||||
}
|
||||
if (y > 0)
|
||||
{
|
||||
queue.Push(b2VoronoiDiagramTask(x, y - 1, i - m_countX, b));
|
||||
}
|
||||
if (x < m_countX - 1)
|
||||
{
|
||||
queue.Push(b2VoronoiDiagramTask(x + 1, y, i + 1, b));
|
||||
}
|
||||
if (y < m_countY - 1)
|
||||
{
|
||||
queue.Push(b2VoronoiDiagramTask(x, y + 1, i + m_countX, b));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void b2VoronoiDiagram::GetNodes(NodeCallback& callback) const
|
||||
{
|
||||
for (int32 y = 0; y < m_countY - 1; y++)
|
||||
{
|
||||
for (int32 x = 0; x < m_countX - 1; x++)
|
||||
{
|
||||
int32 i = x + y * m_countX;
|
||||
const Generator* a = m_diagram[i];
|
||||
const Generator* b = m_diagram[i + 1];
|
||||
const Generator* c = m_diagram[i + m_countX];
|
||||
const Generator* d = m_diagram[i + 1 + m_countX];
|
||||
if (b != c)
|
||||
{
|
||||
if (a != b && a != c &&
|
||||
(a->necessary || b->necessary || c->necessary))
|
||||
{
|
||||
callback(a->tag, b->tag, c->tag);
|
||||
}
|
||||
if (d != b && d != c &&
|
||||
(b->necessary || d->necessary || c->necessary))
|
||||
{
|
||||
callback(b->tag, d->tag, c->tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Google, Inc.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#ifndef B2_VORONOI_DIAGRAM
|
||||
#define B2_VORONOI_DIAGRAM
|
||||
|
||||
#include <Box2D/Common/b2Math.h>
|
||||
|
||||
class b2StackAllocator;
|
||||
struct b2AABB;
|
||||
|
||||
/// A field representing the nearest generator from each point.
|
||||
class b2VoronoiDiagram
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
b2VoronoiDiagram(b2StackAllocator* allocator, int32 generatorCapacity);
|
||||
~b2VoronoiDiagram();
|
||||
|
||||
/// Add a generator.
|
||||
/// @param the position of the generator.
|
||||
/// @param a tag used to identify the generator in callback functions.
|
||||
/// @param whether to callback for nodes associated with the generator.
|
||||
void AddGenerator(const b2Vec2& center, int32 tag, bool necessary);
|
||||
|
||||
/// Generate the Voronoi diagram. It is rasterized with a given interval
|
||||
/// in the same range as the necessary generators exist.
|
||||
/// @param the interval of the diagram.
|
||||
/// @param margin for which the range of the diagram is extended.
|
||||
void Generate(float32 radius, float32 margin);
|
||||
|
||||
/// Callback used by GetNodes().
|
||||
class NodeCallback
|
||||
{
|
||||
public:
|
||||
virtual ~NodeCallback() {}
|
||||
/// Receive tags for generators associated with a node.
|
||||
virtual void operator()(int32 a, int32 b, int32 c) = 0;
|
||||
};
|
||||
|
||||
/// Enumerate all nodes that contain at least one necessary generator.
|
||||
/// @param a callback function object called for each node.
|
||||
void GetNodes(NodeCallback& callback) const;
|
||||
|
||||
private:
|
||||
|
||||
struct Generator
|
||||
{
|
||||
b2Vec2 center;
|
||||
int32 tag;
|
||||
bool necessary;
|
||||
};
|
||||
|
||||
struct b2VoronoiDiagramTask
|
||||
{
|
||||
int32 m_x, m_y, m_i;
|
||||
Generator* m_generator;
|
||||
|
||||
b2VoronoiDiagramTask() {}
|
||||
b2VoronoiDiagramTask(int32 x, int32 y, int32 i, Generator* g)
|
||||
{
|
||||
m_x = x;
|
||||
m_y = y;
|
||||
m_i = i;
|
||||
m_generator = g;
|
||||
}
|
||||
};
|
||||
|
||||
b2StackAllocator *m_allocator;
|
||||
Generator* m_generatorBuffer;
|
||||
int32 m_generatorCapacity;
|
||||
int32 m_generatorCount;
|
||||
int32 m_countX, m_countY;
|
||||
Generator** m_diagram;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user