Liquidfun links

This commit is contained in:
viktorljung
2015-09-09 18:04:17 +01:00
parent 8833377855
commit 67f7a30ca6
184 changed files with 52115 additions and 2 deletions
+65
View File
@@ -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
View File
@@ -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
+37
View File
@@ -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"
+63
View File
@@ -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
+441
View File
@@ -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
View File
@@ -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
View File
@@ -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
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+98
View File
@@ -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
View File
@@ -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
View File
@@ -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