/*
* 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/b2ParticleSystem.h>
#include <Box2D/Particle/b2ParticleGroup.h>
#include <Box2D/Particle/b2VoronoiDiagram.h>
#include <Box2D/Common/b2BlockAllocator.h>
#include <Box2D/Dynamics/b2World.h>
#include <Box2D/Dynamics/b2WorldCallbacks.h>
#include <Box2D/Dynamics/b2Body.h>
#include <Box2D/Dynamics/b2Fixture.h>
#include <Box2D/Collision/Shapes/b2Shape.h>
#include <algorithm>

#include <tms/core/print.h>
#include "creature.hh"

static const uint32 xTruncBits = 12;
static const uint32 yTruncBits = 12;
static const uint32 tagBits = 8 * sizeof(uint32);
static const uint32 yOffset = 1 << (yTruncBits - 1);
static const uint32 yShift = tagBits - yTruncBits;
static const uint32 xShift = tagBits - yTruncBits - xTruncBits;
static const uint32 xScale = 1 << xShift;
static const uint32 xOffset = xScale * (1 << (xTruncBits - 1));
static const uint32 xMask = (1 << xTruncBits) - 1;
static const uint32 yMask = (1 << yTruncBits) - 1;

static inline uint32 computeTag(float32 x, float32 y)
{
	return ((uint32)(y + yOffset) << yShift) + (uint32)(xScale * x + xOffset);
}

static inline uint32 computeRelativeTag(uint32 tag, int32 x, int32 y)
{
	return tag + (y << yShift) + (x << xShift);
}

b2ParticleSystem::b2ParticleSystem()
{

	m_timestamp = 0;
	m_allParticleFlags = 0;
	m_allGroupFlags = 0;
	m_density = 1;
	m_inverseDensity = 1;
	m_gravityScale = 1;
	m_particleDiameter = 1;
	m_inverseDiameter = 1;
	m_squaredDiameter = 1;

	m_count = 0;
	m_internalAllocatedCapacity = 0;
	m_maxCount = 0;
	m_accumulationBuffer = NULL;
	m_accumulation2Buffer = NULL;
	m_depthBuffer = NULL;
	m_groupBuffer = NULL;

	m_proxyCount = 0;
	m_proxyCapacity = 0;
	m_proxyBuffer = NULL;

	m_contactCount = 0;
	m_contactCapacity = 0;
	m_contactBuffer = NULL;

	m_bodyContactCount = 0;
	m_bodyContactCapacity = 0;
	m_bodyContactBuffer = NULL;

	m_pairCount = 0;
	m_pairCapacity = 0;
	m_pairBuffer = NULL;

	m_triadCount = 0;
	m_triadCapacity = 0;
	m_triadBuffer = NULL;

	m_groupCount = 0;
	m_groupList = NULL;

	m_pressureStrength = 0.05f;
	m_dampingStrength = 1.0f;
	m_elasticStrength = 0.25f;
	m_springStrength = 0.25f;
	m_viscousStrength = 0.08f;
	m_surfaceTensionStrengthA = 0.1f/4.f;
	m_surfaceTensionStrengthB = 0.2f/4.f;
	m_powderStrength = 0.5f;
	m_ejectionStrength = 0.5f;
	m_colorMixingStrength = 0.5f;

	m_world = NULL;

}

b2ParticleSystem::~b2ParticleSystem()
{
}

// Reallocate a buffer
template <typename T> T* b2ParticleSystem::ReallocateBuffer(T* oldBuffer, int32 oldCapacity, int32 newCapacity)
{
	b2Assert(newCapacity > oldCapacity);
	T* newBuffer = (T*) m_world->m_blockAllocator.Allocate(sizeof(T) * newCapacity);
	memcpy(newBuffer, oldBuffer, sizeof(T) * oldCapacity);
	m_world->m_blockAllocator.Free(oldBuffer, sizeof(T) * oldCapacity);
	return newBuffer;
}

// Reallocate a buffer
template <typename T> T* b2ParticleSystem::ReallocateBuffer(T* buffer, int32 userSuppliedCapacity, int32 oldCapacity, int32 newCapacity, bool deferred)
{
	b2Assert(newCapacity > oldCapacity);
	// A 'deferred' buffer is reallocated only if it is not NULL.
	// If 'userSuppliedCapacity' is not zero, buffer is user supplied and must be kept.
	b2Assert(!userSuppliedCapacity || newCapacity <= userSuppliedCapacity);
	if ((!deferred || buffer) && !userSuppliedCapacity)
	{
		buffer = ReallocateBuffer(buffer, oldCapacity, newCapacity);
	}
	return buffer;
}

// Reallocate a buffer
template <typename T> T* b2ParticleSystem::ReallocateBuffer(ParticleBuffer<T>* buffer, int32 oldCapacity, int32 newCapacity, bool deferred)
{
	b2Assert(newCapacity > oldCapacity);
	return ReallocateBuffer(buffer->data, buffer->userSuppliedCapacity, oldCapacity, newCapacity, deferred);
}

template <typename T> T* b2ParticleSystem::RequestParticleBuffer(T* buffer)
{
	if (!buffer)
	{
		buffer = (T*) (m_world->m_blockAllocator.Allocate(sizeof(T) * m_internalAllocatedCapacity));
		memset(buffer, 0, sizeof(T) * m_internalAllocatedCapacity);
	}
	return buffer;
}

static int32 LimitCapacity(int32 capacity, int32 maxCount)
{
	return maxCount && capacity > maxCount ? maxCount : capacity;
}

int32 b2ParticleSystem::CreateParticle(const b2ParticleDef& def)
{
    if (m_count >= b2_maxParticles) {
        tms_debugf("reached maximum particles");
        return b2_invalidParticleIndex;
    }

	if (m_count >= m_internalAllocatedCapacity)
	{
		int32 capacity = m_count ? 2 * m_count : b2_minParticleBufferCapacity;
		capacity = LimitCapacity(capacity, m_maxCount);
		capacity = LimitCapacity(capacity, m_flagsBuffer.userSuppliedCapacity);
		capacity = LimitCapacity(capacity, m_positionBuffer.userSuppliedCapacity);
		capacity = LimitCapacity(capacity, m_velocityBuffer.userSuppliedCapacity);
		capacity = LimitCapacity(capacity, m_colorBuffer.userSuppliedCapacity);
		capacity = LimitCapacity(capacity, m_userDataBuffer.userSuppliedCapacity);
		if (m_internalAllocatedCapacity < capacity)
		{
			m_flagsBuffer.data = ReallocateBuffer(&m_flagsBuffer, m_internalAllocatedCapacity, capacity, false);
			m_positionBuffer.data = ReallocateBuffer(&m_positionBuffer, m_internalAllocatedCapacity, capacity, false);
			m_velocityBuffer.data = ReallocateBuffer(&m_velocityBuffer, m_internalAllocatedCapacity, capacity, false);
			m_accumulationBuffer = ReallocateBuffer(m_accumulationBuffer, 0, m_internalAllocatedCapacity, capacity, false);
			m_accumulation2Buffer = ReallocateBuffer(m_accumulation2Buffer, 0, m_internalAllocatedCapacity, capacity, true);

			m_dataBuffer = ReallocateBuffer(m_dataBuffer, 0, m_internalAllocatedCapacity, capacity, false);

			m_depthBuffer = ReallocateBuffer(m_depthBuffer, 0, m_internalAllocatedCapacity, capacity, true);
			m_colorBuffer.data = ReallocateBuffer(&m_colorBuffer, m_internalAllocatedCapacity, capacity, true);
			m_groupBuffer = ReallocateBuffer(m_groupBuffer, 0, m_internalAllocatedCapacity, capacity, false);
			m_userDataBuffer.data = ReallocateBuffer(&m_userDataBuffer, m_internalAllocatedCapacity, capacity, true);
			m_internalAllocatedCapacity = capacity;
		}
	}
	if (m_count >= m_internalAllocatedCapacity)
	{
		return b2_invalidParticleIndex;
	}
	int32 index = m_count++;
	m_flagsBuffer.data[index] = def.flags;
	m_positionBuffer.data[index] = def.position;
	m_velocityBuffer.data[index] = def.velocity;
	m_groupBuffer[index] = NULL;
	if (m_depthBuffer)
	{
		m_depthBuffer[index] = 0;
	}
	if (m_colorBuffer.data || !def.color.IsZero())
	{
		m_colorBuffer.data = RequestParticleBuffer(m_colorBuffer.data);
		m_colorBuffer.data[index] = def.color;
	}
	if (m_userDataBuffer.data || def.userData)
	{
		m_userDataBuffer.data= RequestParticleBuffer(m_userDataBuffer.data);
		m_userDataBuffer.data[index] = def.userData;
	}
	if (m_proxyCount >= m_proxyCapacity)
	{
		int32 oldCapacity = m_proxyCapacity;
		int32 newCapacity = m_proxyCount ? 2 * m_proxyCount : b2_minParticleBufferCapacity;
		m_proxyBuffer = ReallocateBuffer(m_proxyBuffer, oldCapacity, newCapacity);
		m_proxyCapacity = newCapacity;
	}
	m_proxyBuffer[m_proxyCount++].index = index;
	return index;
}

void b2ParticleSystem::DestroyParticle(
	int32 index, bool callDestructionListener)
{
	uint32 flags = b2_zombieParticle;
	if (callDestructionListener)
	{
		flags |= b2_destructionListener;
	}
	m_flagsBuffer.data[index] |= flags;
}

int32 b2ParticleSystem::DestroyParticlesInShape(
	const b2Shape& shape, const b2Transform& xf,
	bool callDestructionListener)
{
	class DestroyParticlesInShapeCallback : public b2QueryCallback
	{
	public:
		DestroyParticlesInShapeCallback(
			b2ParticleSystem* system, const b2Shape& shape,
			const b2Transform& xf, bool callDestructionListener)
		{
			m_system = system;
			m_shape = &shape;
			m_xf = xf;
			m_callDestructionListener = callDestructionListener;
			m_destroyed = 0;
		}

		bool ReportFixture(b2Fixture* fixture)
		{
			return false;
		}

		bool ReportParticle(int32 index)
		{
			b2Assert(index >=0 && index < m_system->m_count);
			if (m_shape->TestPoint(m_xf, m_system->m_positionBuffer.data[index]))
			{
				m_system->DestroyParticle(index, m_callDestructionListener);
				m_destroyed++;
			}
			return true;
		}

		int32 Destroyed() { return m_destroyed; }

	private:
		b2ParticleSystem* m_system;
		const b2Shape* m_shape;
		b2Transform m_xf;
		bool m_callDestructionListener;
		int32 m_destroyed;
	} callback(this, shape, xf, callDestructionListener);
	b2AABB aabb;
	shape.ComputeAABB(&aabb, xf, 0);
	m_world->QueryAABB(&callback, aabb);
	return callback.Destroyed();
}

void b2ParticleSystem::DestroyParticlesInGroup(
	b2ParticleGroup* group, bool callDestructionListener)
{
	for (int32 i = group->m_firstIndex; i < group->m_lastIndex; i++) {
		DestroyParticle(i, callDestructionListener);
	}
}

b2ParticleGroup* b2ParticleSystem::CreateParticleGroup(const b2ParticleGroupDef& groupDef)
{
	float32 stride = GetParticleStride();
	b2Transform identity;
	identity.SetIdentity();
	b2Transform transform = identity;
	int32 firstIndex = m_count;
	if (groupDef.shape)
	{
		b2ParticleDef particleDef;
		particleDef.flags = groupDef.flags;
		particleDef.color = groupDef.color;
		particleDef.userData = groupDef.userData;
		const b2Shape *shape = groupDef.shape;
		transform.Set(groupDef.position, groupDef.angle);
		b2AABB aabb;
		int32 childCount = shape->GetChildCount();
		for (int32 childIndex = 0; childIndex < childCount; childIndex++)
		{
			if (childIndex == 0)
			{
				shape->ComputeAABB(&aabb, identity, childIndex);
			}
			else
			{
				b2AABB childAABB;
				shape->ComputeAABB(&childAABB, identity, childIndex);
				aabb.Combine(childAABB);
			}
		}
		for (float32 y = floorf(aabb.lowerBound.y / stride) * stride; y < aabb.upperBound.y; y += stride)
		{
			for (float32 x = floorf(aabb.lowerBound.x / stride) * stride; x < aabb.upperBound.x; x += stride)
			{
				b2Vec2 p(x, y);
				if (shape->TestPoint(identity, p))
				{
					p = b2Mul(transform, p);
					particleDef.position = p;
					particleDef.velocity =
						groupDef.linearVelocity +
						b2Cross(groupDef.angularVelocity, p - groupDef.position);
					CreateParticle(particleDef);
				}
			}
		}
	}
	int32 lastIndex = m_count;

	void* mem = m_world->m_blockAllocator.Allocate(sizeof(b2ParticleGroup));
	b2ParticleGroup* group = new (mem) b2ParticleGroup();
	group->m_system = this;
	group->m_firstIndex = firstIndex;
	group->m_lastIndex = lastIndex;
	group->m_groupFlags = groupDef.groupFlags;
	group->m_strength = groupDef.strength;
	group->m_userData = groupDef.userData;
	group->m_transform = transform;
	group->m_destroyAutomatically = groupDef.destroyAutomatically;
	group->m_prev = NULL;
	group->m_next = m_groupList;
	if (m_groupList)
	{
		m_groupList->m_prev = group;
	}
	m_groupList = group;
	++m_groupCount;
	for (int32 i = firstIndex; i < lastIndex; i++)
	{
		m_groupBuffer[i] = group;
	}

	UpdateContacts(true);
	if (groupDef.flags & k_pairFlags)
	{
		for (int32 k = 0; k < m_contactCount; k++)
		{
			const b2ParticleContact& contact = m_contactBuffer[k];
			int32 a = contact.indexA;
			int32 b = contact.indexB;
			if (a > b) b2Swap(a, b);
			if (firstIndex <= a && b < lastIndex)
			{
				if (m_pairCount >= m_pairCapacity)
				{
					int32 oldCapacity = m_pairCapacity;
					int32 newCapacity = m_pairCount ? 2 * m_pairCount : b2_minParticleBufferCapacity;
					m_pairBuffer = ReallocateBuffer(m_pairBuffer, oldCapacity, newCapacity);
					m_pairCapacity = newCapacity;
				}
				Pair& pair = m_pairBuffer[m_pairCount];
				pair.indexA = a;
				pair.indexB = b;
				pair.flags = contact.flags;
				pair.strength = groupDef.strength;
				pair.distance = b2Distance(
					m_positionBuffer.data[a],
					m_positionBuffer.data[b]);
				m_pairCount++;
			}
		}
	}
	if (groupDef.flags & k_triadFlags)
	{
		b2VoronoiDiagram diagram(
			&m_world->m_stackAllocator, lastIndex - firstIndex);
		for (int32 i = firstIndex; i < lastIndex; i++)
		{
			diagram.AddGenerator(m_positionBuffer.data[i], i);
		}
		diagram.Generate(stride / 2);
		CreateParticleGroupCallback callback;
		callback.system = this;
		callback.def = &groupDef;
		callback.firstIndex = firstIndex;
		diagram.GetNodes(callback);
	}
	if (groupDef.groupFlags & b2_solidParticleGroup)
	{
		ComputeDepthForGroup(group);
	}

	return group;
}

void b2ParticleSystem::CreateParticleGroupCallback::operator()(int32 a, int32 b, int32 c) const
{
	const b2Vec2& pa = system->m_positionBuffer.data[a];
	const b2Vec2& pb = system->m_positionBuffer.data[b];
	const b2Vec2& pc = system->m_positionBuffer.data[c];
	b2Vec2 dab = pa - pb;
	b2Vec2 dbc = pb - pc;
	b2Vec2 dca = pc - pa;
	float32 maxDistanceSquared = b2_maxTriadDistanceSquared * system->m_squaredDiameter;
	if (b2Dot(dab, dab) < maxDistanceSquared &&
		b2Dot(dbc, dbc) < maxDistanceSquared &&
		b2Dot(dca, dca) < maxDistanceSquared)
	{
		if (system->m_triadCount >= system->m_triadCapacity)
		{
			int32 oldCapacity = system->m_triadCapacity;
			int32 newCapacity = system->m_triadCount ? 2 * system->m_triadCount : b2_minParticleBufferCapacity;
			system->m_triadBuffer = system->ReallocateBuffer(system->m_triadBuffer, oldCapacity, newCapacity);
			system->m_triadCapacity = newCapacity;
		}
		Triad& triad = system->m_triadBuffer[system->m_triadCount];
		triad.indexA = a;
		triad.indexB = b;
		triad.indexC = c;
		triad.flags =
			system->m_flagsBuffer.data[a] |
			system->m_flagsBuffer.data[b] |
			system->m_flagsBuffer.data[c];
		triad.strength = def->strength;
		b2Vec2 midPoint = (float32) 1 / 3 * (pa + pb + pc);
		triad.pa = pa - midPoint;
		triad.pb = pb - midPoint;
		triad.pc = pc - midPoint;
		triad.ka = -b2Dot(dca, dab);
		triad.kb = -b2Dot(dab, dbc);
		triad.kc = -b2Dot(dbc, dca);
		triad.s = b2Cross(pa, pb) + b2Cross(pb, pc) + b2Cross(pc, pa);
		system->m_triadCount++;
	}
};

void b2ParticleSystem::JoinParticleGroups(b2ParticleGroup* groupA, b2ParticleGroup* groupB)
{
	b2Assert(groupA != groupB);
	RotateBuffer(groupB->m_firstIndex, groupB->m_lastIndex, m_count);
	b2Assert(groupB->m_lastIndex == m_count);
	RotateBuffer(groupA->m_firstIndex, groupA->m_lastIndex, groupB->m_firstIndex);
	b2Assert(groupA->m_lastIndex == groupB->m_firstIndex);

	uint32 particleFlags = 0;
	for (int32 i = groupA->m_firstIndex; i < groupB->m_lastIndex; i++)
	{
		particleFlags |= m_flagsBuffer.data[i];
	}

	UpdateContacts(true);
	if (particleFlags & k_pairFlags)
	{
		for (int32 k = 0; k < m_contactCount; k++)
		{
			const b2ParticleContact& contact = m_contactBuffer[k];
			int32 a = contact.indexA;
			int32 b = contact.indexB;
			if (a > b) b2Swap(a, b);
			if (groupA->m_firstIndex <= a && a < groupA->m_lastIndex &&
				groupB->m_firstIndex <= b && b < groupB->m_lastIndex)
			{
				if (m_pairCount >= m_pairCapacity)
				{
					int32 oldCapacity = m_pairCapacity;
					int32 newCapacity = m_pairCount ? 2 * m_pairCount : b2_minParticleBufferCapacity;
					m_pairBuffer = ReallocateBuffer(m_pairBuffer, oldCapacity, newCapacity);
					m_pairCapacity = newCapacity;
				}
				Pair& pair = m_pairBuffer[m_pairCount];
				pair.indexA = a;
				pair.indexB = b;
				pair.flags = contact.flags;
				pair.strength = b2Min(groupA->m_strength, groupB->m_strength);
				pair.distance = b2Distance(m_positionBuffer.data[a], m_positionBuffer.data[b]);
				m_pairCount++;
			}
		}
	}
	if (particleFlags & k_triadFlags)
	{
		b2VoronoiDiagram diagram(
			&m_world->m_stackAllocator,
			groupB->m_lastIndex - groupA->m_firstIndex);
		for (int32 i = groupA->m_firstIndex; i < groupB->m_lastIndex; i++)
		{
			if (!(m_flagsBuffer.data[i] & b2_zombieParticle))
			{
				diagram.AddGenerator(m_positionBuffer.data[i], i);
			}
		}
		diagram.Generate(GetParticleStride() / 2);
		JoinParticleGroupsCallback callback;
		callback.system = this;
		callback.groupA = groupA;
		callback.groupB = groupB;
		diagram.GetNodes(callback);
	}

	for (int32 i = groupB->m_firstIndex; i < groupB->m_lastIndex; i++)
	{
		m_groupBuffer[i] = groupA;
	}
	uint32 groupFlags = groupA->m_groupFlags | groupB->m_groupFlags;
	groupA->m_groupFlags = groupFlags;
	groupA->m_lastIndex = groupB->m_lastIndex;
	groupB->m_firstIndex = groupB->m_lastIndex;
	DestroyParticleGroup(groupB);

	if (groupFlags & b2_solidParticleGroup)
	{
		ComputeDepthForGroup(groupA);
	}
}

void b2ParticleSystem::JoinParticleGroupsCallback::operator()(int32 a, int32 b, int32 c) const
{
	// Create a triad if it will contain particles from both groups.
	int32 countA =
		(a < groupB->m_firstIndex) +
		(b < groupB->m_firstIndex) +
		(c < groupB->m_firstIndex);
	if (countA > 0 && countA < 3)
	{
		uint32 af = system->m_flagsBuffer.data[a];
		uint32 bf = system->m_flagsBuffer.data[b];
		uint32 cf = system->m_flagsBuffer.data[c];
		if (af & bf & cf & k_triadFlags)
		{
			const b2Vec2& pa = system->m_positionBuffer.data[a];
			const b2Vec2& pb = system->m_positionBuffer.data[b];
			const b2Vec2& pc = system->m_positionBuffer.data[c];
			b2Vec2 dab = pa - pb;
			b2Vec2 dbc = pb - pc;
			b2Vec2 dca = pc - pa;
			float32 maxDistanceSquared = b2_maxTriadDistanceSquared * system->m_squaredDiameter;
			if (b2Dot(dab, dab) < maxDistanceSquared &&
				b2Dot(dbc, dbc) < maxDistanceSquared &&
				b2Dot(dca, dca) < maxDistanceSquared)
			{
				if (system->m_triadCount >= system->m_triadCapacity)
				{
					int32 oldCapacity = system->m_triadCapacity;
					int32 newCapacity = system->m_triadCount ? 2 * system->m_triadCount : b2_minParticleBufferCapacity;
					system->m_triadBuffer = system->ReallocateBuffer(system->m_triadBuffer, oldCapacity, newCapacity);
					system->m_triadCapacity = newCapacity;
				}
				Triad& triad = system->m_triadBuffer[system->m_triadCount];
				triad.indexA = a;
				triad.indexB = b;
				triad.indexC = c;
				triad.flags = af | bf | cf;
				triad.strength = b2Min(groupA->m_strength, groupB->m_strength);
				b2Vec2 midPoint = (float32) 1 / 3 * (pa + pb + pc);
				triad.pa = pa - midPoint;
				triad.pb = pb - midPoint;
				triad.pc = pc - midPoint;
				triad.ka = -b2Dot(dca, dab);
				triad.kb = -b2Dot(dab, dbc);
				triad.kc = -b2Dot(dbc, dca);
				triad.s = b2Cross(pa, pb) + b2Cross(pb, pc) + b2Cross(pc, pa);
				system->m_triadCount++;
			}
		}
	}
};

// Only called from SolveZombie() or JoinParticleGroups().
void b2ParticleSystem::DestroyParticleGroup(b2ParticleGroup* group)
{
	b2Assert(m_groupCount > 0);
	b2Assert(group);

	if (m_world->m_destructionListener)
	{
		m_world->m_destructionListener->SayGoodbye(group);
	}

	for (int32 i = group->m_firstIndex; i < group->m_lastIndex; i++)
	{
		m_groupBuffer[i] = NULL;
	}

	if (group->m_prev)
	{
		group->m_prev->m_next = group->m_next;
	}
	if (group->m_next)
	{
		group->m_next->m_prev = group->m_prev;
	}
	if (group == m_groupList)
	{
		m_groupList = group->m_next;
	}

	--m_groupCount;
	group->~b2ParticleGroup();
	m_world->m_blockAllocator.Free(group, sizeof(b2ParticleGroup));
}

void b2ParticleSystem::ComputeDepthForGroup(b2ParticleGroup* group)
{
	for (int32 i = group->m_firstIndex; i < group->m_lastIndex; i++)
	{
		m_accumulationBuffer[i] = 0;
	}
	for (int32 k = 0; k < m_contactCount; k++)
	{
		const b2ParticleContact& contact = m_contactBuffer[k];
		int32 a = contact.indexA;
		int32 b = contact.indexB;
		if (a >= group->m_firstIndex && a < group->m_lastIndex &&
			b >= group->m_firstIndex && b < group->m_lastIndex)
		{
			float32 w = contact.weight;
			m_accumulationBuffer[a] += w;
			m_accumulationBuffer[b] += w;
		}
	}
	m_depthBuffer = RequestParticleBuffer(m_depthBuffer);
	for (int32 i = group->m_firstIndex; i < group->m_lastIndex; i++)
	{
		float32 w = m_accumulationBuffer[i];
		m_depthBuffer[i] = w < 0.8f ? 0 : b2_maxFloat;
	}
	int32 interationCount = group->GetParticleCount();
	for (int32 t = 0; t < interationCount; t++)
	{
		bool updated = false;
		for (int32 k = 0; k < m_contactCount; k++)
		{
			const b2ParticleContact& contact = m_contactBuffer[k];
			int32 a = contact.indexA;
			int32 b = contact.indexB;
			if (a >= group->m_firstIndex && a < group->m_lastIndex &&
				b >= group->m_firstIndex && b < group->m_lastIndex)
			{
				float32 r = 1 - contact.weight;
				float32& ap0 = m_depthBuffer[a];
				float32& bp0 = m_depthBuffer[b];
				float32 ap1 = bp0 + r;
				float32 bp1 = ap0 + r;
				if (ap0 > ap1)
				{
					ap0 = ap1;
					updated = true;
				}
				if (bp0 > bp1)
				{
					bp0 = bp1;
					updated = true;
				}
			}
		}
		if (!updated)
		{
			break;
		}
	}
	for (int32 i = group->m_firstIndex; i < group->m_lastIndex; i++)
	{
		float32& p = m_depthBuffer[i];
		if (p < b2_maxFloat)
		{
			p *= m_particleDiameter;
		}
		else
		{
			p = 0;
		}
	}

}

inline void b2ParticleSystem::AddContact(int32 a, int32 b)
{
    /* check principia layer filtering */
    if ((m_flagsBuffer.data[a] & m_flagsBuffer.data[b]) < (1<<16))
        return;

	b2Vec2 d = m_positionBuffer.data[b] - m_positionBuffer.data[a];
	float32 d2 = b2Dot(d, d);
	if (d2 < m_squaredDiameter)
	{
		if (m_contactCount >= m_contactCapacity)
		{
			int32 oldCapacity = m_contactCapacity;
			int32 newCapacity = m_contactCount ? 2 * m_contactCount : b2_minParticleBufferCapacity;
			m_contactBuffer = ReallocateBuffer(m_contactBuffer, oldCapacity, newCapacity);
			m_contactCapacity = newCapacity;
		}
		float32 invD = b2InvSqrt(d2);
		b2ParticleContact& contact = m_contactBuffer[m_contactCount];
		contact.indexA = a;
		contact.indexB = b;
		contact.flags = m_flagsBuffer.data[a] | m_flagsBuffer.data[b];
		contact.weight = 1 - d2 * invD * m_inverseDiameter;
		contact.normal = invD * d;
		m_contactCount++;
	}
}

static bool b2ParticleContactIsZombie(const b2ParticleContact& contact)
{
	return (contact.flags & b2_zombieParticle) == b2_zombieParticle;
}

void b2ParticleSystem::UpdateContacts(bool exceptZombie)
{
	Proxy* beginProxy = m_proxyBuffer;
	Proxy* endProxy = beginProxy + m_proxyCount;
	for (Proxy* proxy = beginProxy; proxy < endProxy; ++proxy)
	{
		int32 i = proxy->index;
		b2Vec2 p = m_positionBuffer.data[i];
		proxy->tag = computeTag(m_inverseDiameter * p.x, m_inverseDiameter * p.y);
	}
	std::sort(beginProxy, endProxy);
	m_contactCount = 0;
	for (Proxy *a = beginProxy, *c = beginProxy; a < endProxy; a++)
	{
		uint32 rightTag = computeRelativeTag(a->tag, 1, 0);
		for (Proxy* b = a + 1; b < endProxy; b++)
		{
			if (rightTag < b->tag) break;
			AddContact(a->index, b->index);
		}
		uint32 bottomLeftTag = computeRelativeTag(a->tag, -1, 1);
		for (; c < endProxy; c++)
		{
			if (bottomLeftTag <= c->tag) break;
		}
		uint32 bottomRightTag = computeRelativeTag(a->tag, 1, 1);
		for (Proxy* b = c; b < endProxy; b++)
		{
			if (bottomRightTag < b->tag) break;
			AddContact(a->index, b->index);
		}
	}
	if (exceptZombie)
	{
		b2ParticleContact* lastContact = std::remove_if(
			m_contactBuffer, m_contactBuffer + m_contactCount,
			b2ParticleContactIsZombie);
		m_contactCount = (int32) (lastContact - m_contactBuffer);
	}
}

void b2ParticleSystem::UpdateBodyContacts()
{
	b2AABB aabb;
	aabb.lowerBound.x = +b2_maxFloat;
	aabb.lowerBound.y = +b2_maxFloat;
	aabb.upperBound.x = -b2_maxFloat;
	aabb.upperBound.y = -b2_maxFloat;
	for (int32 i = 0; i < m_count; i++)
	{
		b2Vec2 p = m_positionBuffer.data[i];
		aabb.lowerBound = b2Min(aabb.lowerBound, p);
		aabb.upperBound = b2Max(aabb.upperBound, p);
	}
	aabb.lowerBound.x -= m_particleDiameter;
	aabb.lowerBound.y -= m_particleDiameter;
	aabb.upperBound.x += m_particleDiameter;
	aabb.upperBound.y += m_particleDiameter;
	m_bodyContactCount = 0;
	class UpdateBodyContactsCallback : public b2QueryCallback
	{
		bool ReportFixture(b2Fixture* fixture)
		{
			if (fixture->IsSensor())
			{
				return true;
			}
			const b2Shape* shape = fixture->GetShape();
			b2Body* b = fixture->GetBody();
			b2Vec2 bp = b->GetWorldCenter();
			float32 bm = b->GetMass();
			float32 bI = b->GetInertia() - bm * b->GetLocalCenter().LengthSquared();
			float32 invBm = bm > 0 ? 1 / bm : 0;
			float32 invBI = bI > 0 ? 1 / bI : 0;
			int32 childCount = shape->GetChildCount();
			for (int32 childIndex = 0; childIndex < childCount; childIndex++)
			{
				b2AABB aabb = fixture->GetAABB(childIndex);
				aabb.lowerBound.x -= m_system->m_particleDiameter;
				aabb.lowerBound.y -= m_system->m_particleDiameter;
				aabb.upperBound.x += m_system->m_particleDiameter;
				aabb.upperBound.y += m_system->m_particleDiameter;
				Proxy* beginProxy = m_system->m_proxyBuffer;
				Proxy* endProxy = beginProxy + m_system->m_proxyCount;
				Proxy* firstProxy = std::lower_bound(
					beginProxy, endProxy,
					computeTag(
						m_system->m_inverseDiameter * aabb.lowerBound.x,
						m_system->m_inverseDiameter * aabb.lowerBound.y));
				Proxy* lastProxy = std::upper_bound(
					firstProxy, endProxy,
					computeTag(
						m_system->m_inverseDiameter * aabb.upperBound.x,
						m_system->m_inverseDiameter * aabb.upperBound.y));
				for (Proxy* proxy = firstProxy; proxy != lastProxy; ++proxy)
				{
					int32 a = proxy->index;

                    /* check principia layer filtering */
                    if (!((m_system->m_flagsBuffer.data[a] >> 16) & fixture->m_filter.categoryBits))
                        continue;

                    {
                        /* do not collide with robot feet */
                        entity *e = (entity*)fixture->GetUserData();
                        if (e) {
                            if (e->flag_active(ENTITY_IS_CREATURE) && ((creature*)e)->is_foot_fixture(fixture))
                                continue;
                        }
                    }

					b2Vec2 ap = m_system->m_positionBuffer.data[a];
					if (aabb.lowerBound.x <= ap.x && ap.x <= aabb.upperBound.x &&
						aabb.lowerBound.y <= ap.y && ap.y <= aabb.upperBound.y)
					{
						float32 d;
						b2Vec2 n;
						fixture->ComputeDistance(ap, &d, &n, childIndex);
						if (d < m_system->m_particleDiameter)
						{
							float32 invAm =
								m_system->m_flagsBuffer.data[a] & b2_wallParticle ?
								0 : m_system->GetParticleInvMass();
							b2Vec2 rp = ap - bp;
							float32 rpn = b2Cross(rp, n);
							if (m_system->m_bodyContactCount >= m_system->m_bodyContactCapacity)
							{
								int32 oldCapacity = m_system->m_bodyContactCapacity;
								int32 newCapacity = m_system->m_bodyContactCount ? 2 * m_system->m_bodyContactCount : b2_minParticleBufferCapacity;
								m_system->m_bodyContactBuffer = m_system->ReallocateBuffer(m_system->m_bodyContactBuffer, oldCapacity, newCapacity);
								m_system->m_bodyContactCapacity = newCapacity;
							}
							b2ParticleBodyContact& contact = m_system->m_bodyContactBuffer[m_system->m_bodyContactCount];
							contact.index = a;
							contact.body = b;
							contact.weight = 1 - d * m_system->m_inverseDiameter;
							contact.normal = -n;
							contact.mass = 1 / (invAm + invBm + invBI * rpn * rpn);
							m_system->m_bodyContactCount++;
						}
					}
				}
			}
			return true;
		}

		b2ParticleSystem* m_system;

	public:
		UpdateBodyContactsCallback(b2ParticleSystem* system)
		{
			m_system = system;
		}
	} callback(this);
	m_world->QueryAABB(&callback, aabb);
}

void b2ParticleSystem::SolveCollision(const b2TimeStep& step)
{
	b2AABB aabb;
	aabb.lowerBound.x = +b2_maxFloat;
	aabb.lowerBound.y = +b2_maxFloat;
	aabb.upperBound.x = -b2_maxFloat;
	aabb.upperBound.y = -b2_maxFloat;
	for (int32 i = 0; i < m_count; i++)
	{
		b2Vec2 v = m_velocityBuffer.data[i];
		b2Vec2 p1 = m_positionBuffer.data[i];
		b2Vec2 p2 = p1 + step.dt * v;
		aabb.lowerBound = b2Min(aabb.lowerBound, b2Min(p1, p2));
		aabb.upperBound = b2Max(aabb.upperBound, b2Max(p1, p2));
	}
	class SolveCollisionCallback : public b2QueryCallback
	{
		bool ReportFixture(b2Fixture* fixture)
		{
			if (fixture->IsSensor())
			{
				return true;
			}
			const b2Shape* shape = fixture->GetShape();
			b2Body* body = fixture->GetBody();
			Proxy* beginProxy = m_system->m_proxyBuffer;
			Proxy* endProxy = beginProxy + m_system->m_proxyCount;
			int32 childCount = shape->GetChildCount();
			for (int32 childIndex = 0; childIndex < childCount; childIndex++)
			{
				b2AABB aabb = fixture->GetAABB(childIndex);
				aabb.lowerBound.x -= m_system->m_particleDiameter;
				aabb.lowerBound.y -= m_system->m_particleDiameter;
				aabb.upperBound.x += m_system->m_particleDiameter;
				aabb.upperBound.y += m_system->m_particleDiameter;
				Proxy* firstProxy = std::lower_bound(
					beginProxy, endProxy,
					computeTag(
						m_system->m_inverseDiameter * aabb.lowerBound.x,
						m_system->m_inverseDiameter * aabb.lowerBound.y));
				Proxy* lastProxy = std::upper_bound(
					firstProxy, endProxy,
					computeTag(
						m_system->m_inverseDiameter * aabb.upperBound.x,
						m_system->m_inverseDiameter * aabb.upperBound.y));
				for (Proxy* proxy = firstProxy; proxy != lastProxy; ++proxy)
				{
					int32 a = proxy->index;

                    /* check principia layer filtering */
                    if (!((m_system->m_flagsBuffer.data[a] >> 16) & fixture->m_filter.categoryBits))
                        continue;

                    {
                        /* do not collide with robot feet */
                        entity *e = (entity*)fixture->GetUserData();
                        if (e) {
                            if (e->flag_active(ENTITY_IS_CREATURE) && ((creature*)e)->is_foot_fixture(fixture))
                                continue;
                        }
                    }

					b2Vec2 ap = m_system->m_positionBuffer.data[a];
					if (aabb.lowerBound.x <= ap.x && ap.x <= aabb.upperBound.x &&
						aabb.lowerBound.y <= ap.y && ap.y <= aabb.upperBound.y)
					{
						b2Vec2 av = m_system->m_velocityBuffer.data[a];
						b2RayCastOutput output;
						b2RayCastInput input;
						input.p1 = b2Mul(body->m_xf, b2MulT(body->m_xf0, ap));
						input.p2 = ap + m_step.dt * av;
						input.maxFraction = 1;
						if (fixture->RayCast(&output, input, childIndex))
						{
							b2Vec2 p =
								(1 - output.fraction) * input.p1 +
								output.fraction * input.p2 +
								b2_linearSlop * output.normal;
							b2Vec2 v = m_step.inv_dt * (p - ap);
							m_system->m_velocityBuffer.data[a] = v;
							b2Vec2 f = m_system->GetParticleMass() * (av - v);
							f = b2Dot(f, output.normal) * output.normal;
							body->ApplyLinearImpulse(f, p, true);
						}
					}
				}
			}
			return true;
		}

		b2ParticleSystem* m_system;
		b2TimeStep m_step;

	public:
		SolveCollisionCallback(b2ParticleSystem* system, const b2TimeStep& step)
		{
			m_system = system;
			m_step = step;
		}
	} callback(this, step);
	m_world->QueryAABB(&callback, aabb);
}

void b2ParticleSystem::Solve(const b2TimeStep& step)
{
	++m_timestamp;
	if (m_count == 0)
	{
		return;
	}
	m_allParticleFlags = 0;
	for (int32 i = 0; i < m_count; i++)
	{
		m_allParticleFlags |= m_flagsBuffer.data[i];
	}
	if (m_allParticleFlags & b2_zombieParticle)
	{
		SolveZombie();
	}
	m_allGroupFlags = 0;
	for (const b2ParticleGroup* group = m_groupList; group; group = group->GetNext())
	{
		m_allGroupFlags |= group->m_groupFlags;
	}
	b2Vec2 gravity = step.dt * m_gravityScale * m_world->GetGravity();
	float32 criticalVelocytySquared = GetCriticalVelocitySquared(step);
	for (int32 i = 0; i < m_count; i++)
	{
		b2Vec2& v = m_velocityBuffer.data[i];
		v += gravity;
		float32 v2 = b2Dot(v, v);
		if (v2 > criticalVelocytySquared)
		{
			v *= b2Sqrt(criticalVelocytySquared / v2);
		}
	}
	SolveCollision(step);
	if (m_allGroupFlags & b2_rigidParticleGroup)
	{
		SolveRigid(step);
	}
	if (m_allParticleFlags & b2_wallParticle)
	{
		SolveWall(step);
	}
	for (int32 i = 0; i < m_count; i++)
	{
		m_positionBuffer.data[i] += step.dt * m_velocityBuffer.data[i];
		m_dataBuffer[i] *= .99f;
	}
	UpdateBodyContacts();
	UpdateContacts(false);
	if (m_allParticleFlags & b2_viscousParticle)
	{
		SolveViscous(step);
	}
	if (m_allParticleFlags & b2_powderParticle)
	{
		SolvePowder(step);
	}
	if (m_allParticleFlags & b2_tensileParticle)
	{
		SolveTensile(step);
	}
	if (m_allParticleFlags & b2_elasticParticle)
	{
		SolveElastic(step);
	}
	if (m_allParticleFlags & b2_springParticle)
	{
		SolveSpring(step);
	}
	if (m_allGroupFlags & b2_solidParticleGroup)
	{
		SolveSolid(step);
	}
	if (m_allParticleFlags & b2_colorMixingParticle)
	{
		SolveColorMixing(step);
	}
	SolvePressure(step);
	SolveDamping(step);
}

void b2ParticleSystem::SolvePressure(const b2TimeStep& step)
{
	// calculates the sum of contact-weights for each particle
	// that means dimensionless density
	for (int32 i = 0; i < m_count; i++)
	{
		m_accumulationBuffer[i] = 0;
        m_dataBuffer[i] = 0;
	}
	for (int32 k = 0; k < m_bodyContactCount; k++)
	{
		const b2ParticleBodyContact& contact = m_bodyContactBuffer[k];
		int32 a = contact.index;
		float32 w = contact.weight;
		m_accumulationBuffer[a] += w;
	}
	for (int32 k = 0; k < m_contactCount; k++)
	{
		const b2ParticleContact& contact = m_contactBuffer[k];
		int32 a = contact.indexA;
		int32 b = contact.indexB;
		float32 w = contact.weight;
		m_accumulationBuffer[a] += w;
		m_accumulationBuffer[b] += w;

        /*
		m_dataBuffer[a] += w;
		m_dataBuffer[b] += w;
        */
	}
	// ignores powder particles
	if (m_allParticleFlags & k_noPressureFlags)
	{
		for (int32 i = 0; i < m_count; i++)
		{
			if (m_flagsBuffer.data[i] & k_noPressureFlags)
			{
				m_accumulationBuffer[i] = 0;
			}
		}
	}
	// calculates pressure as a linear function of density
	float32 pressurePerWeight = m_pressureStrength * GetCriticalPressure(step);
	for (int32 i = 0; i < m_count; i++)
	{
		float32 w = m_accumulationBuffer[i];
		float32 h = pressurePerWeight * b2Max(0.0f, b2Min(w, b2_maxParticleWeight) - b2_minParticleWeight);
		m_accumulationBuffer[i] = h;
        m_dataBuffer[i] = w;
	}
	// applies pressure between each particles in contact
	float32 velocityPerPressure = step.dt / (m_density * m_particleDiameter);
	for (int32 k = 0; k < m_bodyContactCount; k++)
	{
		const b2ParticleBodyContact& contact = m_bodyContactBuffer[k];
		int32 a = contact.index;
		b2Body* b = contact.body;
		float32 w = contact.weight;
		float32 m = contact.mass;
		b2Vec2 n = contact.normal;
		b2Vec2 p = m_positionBuffer.data[a];
		float32 h = m_accumulationBuffer[a] + pressurePerWeight * w;
		b2Vec2 f = velocityPerPressure * w * m * h * n;
		m_velocityBuffer.data[a] -= GetParticleInvMass() * f;
		b->ApplyLinearImpulse(f, p, true);
	}
	for (int32 k = 0; k < m_contactCount; k++)
	{
		const b2ParticleContact& contact = m_contactBuffer[k];
		int32 a = contact.indexA;
		int32 b = contact.indexB;
		float32 w = contact.weight;
		b2Vec2 n = contact.normal;
		float32 h = m_accumulationBuffer[a] + m_accumulationBuffer[b];
		b2Vec2 f = velocityPerPressure * w * h * n;
		m_velocityBuffer.data[a] -= f;
		m_velocityBuffer.data[b] += f;
	}
}

void b2ParticleSystem::SolveDamping(const b2TimeStep& step)
{
	// reduces normal velocity of each contact
	float32 damping = m_dampingStrength;
	for (int32 k = 0; k < m_bodyContactCount; k++)
	{
		const b2ParticleBodyContact& contact = m_bodyContactBuffer[k];
		int32 a = contact.index;
		b2Body* b = contact.body;
		float32 w = contact.weight;
		float32 m = contact.mass;
		b2Vec2 n = contact.normal;
		b2Vec2 p = m_positionBuffer.data[a];
		b2Vec2 v = b->GetLinearVelocityFromWorldPoint(p) - m_velocityBuffer.data[a];
		float32 vn = b2Dot(v, n);
		if (vn < 0)
		{
			b2Vec2 f = damping * w * m * vn * n;
			m_velocityBuffer.data[a] += GetParticleInvMass() * f;
			b->ApplyLinearImpulse(-f, p, true);
		}
	}
	for (int32 k = 0; k < m_contactCount; k++)
	{
		const b2ParticleContact& contact = m_contactBuffer[k];
		int32 a = contact.indexA;
		int32 b = contact.indexB;
		float32 w = contact.weight;
		b2Vec2 n = contact.normal;
		b2Vec2 v = m_velocityBuffer.data[b] - m_velocityBuffer.data[a];
		float32 vn = b2Dot(v, n);
		if (vn < 0)
		{
			b2Vec2 f = damping * w * vn * n;
			m_velocityBuffer.data[a] += f;
			m_velocityBuffer.data[b] -= f;
		}
	}
}

void b2ParticleSystem::SolveWall(const b2TimeStep& step)
{
	for (int32 i = 0; i < m_count; i++)
	{
		if (m_flagsBuffer.data[i] & b2_wallParticle)
		{
			m_velocityBuffer.data[i].SetZero();
		}
	}
}

void b2ParticleSystem::SolveRigid(const b2TimeStep& step)
{
	for (b2ParticleGroup* group = m_groupList; group; group = group->GetNext())
	{
		if (group->m_groupFlags & b2_rigidParticleGroup)
		{
			group->UpdateStatistics();
			b2Rot rotation(step.dt * group->m_angularVelocity);
			b2Transform transform(
				group->m_center + step.dt * group->m_linearVelocity -
				b2Mul(rotation, group->m_center), rotation);
			group->m_transform = b2Mul(transform, group->m_transform);
			b2Transform velocityTransform;
			velocityTransform.p.x = step.inv_dt * transform.p.x;
			velocityTransform.p.y = step.inv_dt * transform.p.y;
			velocityTransform.q.s = step.inv_dt * transform.q.s;
			velocityTransform.q.c = step.inv_dt * (transform.q.c - 1);
			for (int32 i = group->m_firstIndex; i < group->m_lastIndex; i++)
			{
				m_velocityBuffer.data[i] = b2Mul(velocityTransform, m_positionBuffer.data[i]);
			}
		}
	}
}

void b2ParticleSystem::SolveElastic(const b2TimeStep& step)
{
	float32 elasticStrength = step.inv_dt * m_elasticStrength;
	for (int32 k = 0; k < m_triadCount; k++)
	{
		const Triad& triad = m_triadBuffer[k];
		if (triad.flags & b2_elasticParticle)
		{
			int32 a = triad.indexA;
			int32 b = triad.indexB;
			int32 c = triad.indexC;
			const b2Vec2& oa = triad.pa;
			const b2Vec2& ob = triad.pb;
			const b2Vec2& oc = triad.pc;
			const b2Vec2& pa = m_positionBuffer.data[a];
			const b2Vec2& pb = m_positionBuffer.data[b];
			const b2Vec2& pc = m_positionBuffer.data[c];
			b2Vec2 p = (float32) 1 / 3 * (pa + pb + pc);
			b2Rot r;
			r.s = b2Cross(oa, pa) + b2Cross(ob, pb) + b2Cross(oc, pc);
			r.c = b2Dot(oa, pa) + b2Dot(ob, pb) + b2Dot(oc, pc);
			float32 r2 = r.s * r.s + r.c * r.c;
			float32 invR = b2InvSqrt(r2);
			r.s *= invR;
			r.c *= invR;
			float32 strength = elasticStrength * triad.strength;
			m_velocityBuffer.data[a] += strength * (b2Mul(r, oa) - (pa - p));
			m_velocityBuffer.data[b] += strength * (b2Mul(r, ob) - (pb - p));
			m_velocityBuffer.data[c] += strength * (b2Mul(r, oc) - (pc - p));
		}
	}
}

void b2ParticleSystem::SolveSpring(const b2TimeStep& step)
{
	float32 springStrength = step.inv_dt * m_springStrength;
	for (int32 k = 0; k < m_pairCount; k++)
	{
		const Pair& pair = m_pairBuffer[k];
		if (pair.flags & b2_springParticle)
		{
			int32 a = pair.indexA;
			int32 b = pair.indexB;
			b2Vec2 d = m_positionBuffer.data[b] - m_positionBuffer.data[a];
			float32 r0 = pair.distance;
			float32 r1 = d.Length();
			float32 strength = springStrength * pair.strength;
			b2Vec2 f = strength * (r0 - r1) / r1 * d;
			m_velocityBuffer.data[a] -= f;
			m_velocityBuffer.data[b] += f;
		}
	}
}

void b2ParticleSystem::SolveTensile(const b2TimeStep& step)
{
	m_accumulation2Buffer = RequestParticleBuffer(m_accumulation2Buffer);
	for (int32 i = 0; i < m_count; i++)
	{
		m_accumulationBuffer[i] = 0;
		m_accumulation2Buffer[i] = b2Vec2_zero;
	}
	for (int32 k = 0; k < m_contactCount; k++)
	{
		const b2ParticleContact& contact = m_contactBuffer[k];
		if (contact.flags & b2_tensileParticle)
		{
			int32 a = contact.indexA;
			int32 b = contact.indexB;
			float32 w = contact.weight;
			b2Vec2 n = contact.normal;
			m_accumulationBuffer[a] += w;
			m_accumulationBuffer[b] += w;
			m_accumulation2Buffer[a] -= (1 - w) * w * n;
			m_accumulation2Buffer[b] += (1 - w) * w * n;
		}
	}
	float32 strengthA = m_surfaceTensionStrengthA * GetCriticalVelocity(step);
	float32 strengthB = m_surfaceTensionStrengthB * GetCriticalVelocity(step);
	for (int32 k = 0; k < m_contactCount; k++)
	{
		const b2ParticleContact& contact = m_contactBuffer[k];
		if (contact.flags & b2_tensileParticle)
		{
			int32 a = contact.indexA;
			int32 b = contact.indexB;
			float32 w = contact.weight;
			b2Vec2 n = contact.normal;
			float32 h = m_accumulationBuffer[a] + m_accumulationBuffer[b];
			b2Vec2 s = m_accumulation2Buffer[b] - m_accumulation2Buffer[a];
			float32 fn = (strengthA * (h - 2) + strengthB * b2Dot(s, n)) * w;
			b2Vec2 f = fn * n;
			m_velocityBuffer.data[a] -= f;
			m_velocityBuffer.data[b] += f;
		}
	}
}

void b2ParticleSystem::SolveViscous(const b2TimeStep& step)
{
	float32 viscousStrength = m_viscousStrength;
	for (int32 k = 0; k < m_bodyContactCount; k++)
	{
		const b2ParticleBodyContact& contact = m_bodyContactBuffer[k];
		int32 a = contact.index;
		if (m_flagsBuffer.data[a] & b2_viscousParticle)
		{
			b2Body* b = contact.body;
			float32 w = contact.weight;
			float32 m = contact.mass;
			b2Vec2 p = m_positionBuffer.data[a];
			b2Vec2 v = b->GetLinearVelocityFromWorldPoint(p) - m_velocityBuffer.data[a];
			b2Vec2 f = viscousStrength * m * w * v;
			m_velocityBuffer.data[a] += GetParticleInvMass() * f;
			b->ApplyLinearImpulse(-f, p, true);
		}
	}
	for (int32 k = 0; k < m_contactCount; k++)
	{
		const b2ParticleContact& contact = m_contactBuffer[k];
		if (contact.flags & b2_viscousParticle)
		{
			int32 a = contact.indexA;
			int32 b = contact.indexB;
			float32 w = contact.weight;
			b2Vec2 v = m_velocityBuffer.data[b] - m_velocityBuffer.data[a];
			b2Vec2 f = viscousStrength * w * v;
			m_velocityBuffer.data[a] += f;
			m_velocityBuffer.data[b] -= f;
		}
	}
}

void b2ParticleSystem::SolvePowder(const b2TimeStep& step)
{
	float32 powderStrength = m_powderStrength * GetCriticalVelocity(step);
	float32 minWeight = 1.0f - b2_particleStride;
	for (int32 k = 0; k < m_bodyContactCount; k++)
	{
		const b2ParticleBodyContact& contact = m_bodyContactBuffer[k];
		int32 a = contact.index;
		if (m_flagsBuffer.data[a] & b2_powderParticle)
		{
			float32 w = contact.weight;
			if (w > minWeight)
			{
				b2Body* b = contact.body;
				float32 m = contact.mass;
				b2Vec2 p = m_positionBuffer.data[a];
				b2Vec2 n = contact.normal;
				b2Vec2 f = powderStrength * m * (w - minWeight) * n;
				m_velocityBuffer.data[a] -= GetParticleInvMass() * f;
				b->ApplyLinearImpulse(f, p, true);
			}
		}
	}
	for (int32 k = 0; k < m_contactCount; k++)
	{
		const b2ParticleContact& contact = m_contactBuffer[k];
		if (contact.flags & b2_powderParticle)
		{
			float32 w = contact.weight;
			if (w > minWeight)
			{
				int32 a = contact.indexA;
				int32 b = contact.indexB;
				b2Vec2 n = contact.normal;
				b2Vec2 f = powderStrength * (w - minWeight) * n;
				m_velocityBuffer.data[a] -= f;
				m_velocityBuffer.data[b] += f;
			}
		}
	}
}

void b2ParticleSystem::SolveSolid(const b2TimeStep& step)
{
	// applies extra repulsive force from solid particle groups
	m_depthBuffer = RequestParticleBuffer(m_depthBuffer);
	float32 ejectionStrength = step.inv_dt * m_ejectionStrength;
	for (int32 k = 0; k < m_contactCount; k++)
	{
		const b2ParticleContact& contact = m_contactBuffer[k];
		int32 a = contact.indexA;
		int32 b = contact.indexB;
		if (m_groupBuffer[a] != m_groupBuffer[b])
		{
			float32 w = contact.weight;
			b2Vec2 n = contact.normal;
			float32 h = m_depthBuffer[a] + m_depthBuffer[b];
			b2Vec2 f = ejectionStrength * h * w * n;
			m_velocityBuffer.data[a] -= f;
			m_velocityBuffer.data[b] += f;
		}
	}
}

void b2ParticleSystem::SolveColorMixing(const b2TimeStep& step)
{
	// mixes color between contacting particles
	m_colorBuffer.data = RequestParticleBuffer(m_colorBuffer.data);
	int32 colorMixing256 = (int32) (256 * m_colorMixingStrength);
	for (int32 k = 0; k < m_contactCount; k++)
	{
		const b2ParticleContact& contact = m_contactBuffer[k];
		int32 a = contact.indexA;
		int32 b = contact.indexB;
		if (m_flagsBuffer.data[a] & m_flagsBuffer.data[b] & b2_colorMixingParticle)
		{
			b2ParticleColor& colorA = m_colorBuffer.data[a];
			b2ParticleColor& colorB = m_colorBuffer.data[b];
			int32 dr = (colorMixing256 * (colorB.r - colorA.r)) >> 8;
			int32 dg = (colorMixing256 * (colorB.g - colorA.g)) >> 8;
			int32 db = (colorMixing256 * (colorB.b - colorA.b)) >> 8;
			int32 da = (colorMixing256 * (colorB.a - colorA.a)) >> 8;
			colorA.r += dr;
			colorA.g += dg;
			colorA.b += db;
			colorA.a += da;
			colorB.r -= dr;
			colorB.g -= dg;
			colorB.b -= db;
			colorB.a -= da;
		}
	}
}

void b2ParticleSystem::SolveZombie()
{
	// removes particles with zombie flag
	int32 newCount = 0;
	int32* newIndices = (int32*) m_world->m_stackAllocator.Allocate(sizeof(int32) * m_count);
	for (int32 i = 0; i < m_count; i++)
	{
		int32 flags = m_flagsBuffer.data[i];
		if (flags & b2_zombieParticle)
		{
			b2DestructionListener * const destructionListener =
				m_world->m_destructionListener;
			if ((flags & b2_destructionListener) &&
				destructionListener)
			{
				destructionListener->SayGoodbye(i);
			}
			newIndices[i] = b2_invalidParticleIndex;
		}
		else
		{
			newIndices[i] = newCount;
			if (i != newCount)
			{
				m_flagsBuffer.data[newCount] = m_flagsBuffer.data[i];
				m_positionBuffer.data[newCount] = m_positionBuffer.data[i];
				m_velocityBuffer.data[newCount] = m_velocityBuffer.data[i];
				m_groupBuffer[newCount] = m_groupBuffer[i];
				if (m_depthBuffer)
				{
					m_depthBuffer[newCount] = m_depthBuffer[i];
				}
				if (m_colorBuffer.data)
				{
					m_colorBuffer.data[newCount] = m_colorBuffer.data[i];
				}
				if (m_userDataBuffer.data)
				{
					m_userDataBuffer.data[newCount] = m_userDataBuffer.data[i];
				}
			}
			newCount++;
		}
	}

	// predicate functions
	struct Test
	{
		static bool IsProxyInvalid(const Proxy& proxy)
		{
			return proxy.index < 0;
		}
		static bool IsContactInvalid(const b2ParticleContact& contact)
		{
			return contact.indexA < 0 || contact.indexB < 0;
		}
		static bool IsBodyContactInvalid(const b2ParticleBodyContact& contact)
		{
			return contact.index < 0;
		}
		static bool IsPairInvalid(const Pair& pair)
		{
			return pair.indexA < 0 || pair.indexB < 0;
		}
		static bool IsTriadInvalid(const Triad& triad)
		{
			return triad.indexA < 0 || triad.indexB < 0 || triad.indexC < 0;
		}
	};

	// update proxies
	for (int32 k = 0; k < m_proxyCount; k++)
	{
		Proxy& proxy = m_proxyBuffer[k];
		proxy.index = newIndices[proxy.index];
	}
	Proxy* lastProxy = std::remove_if(
		m_proxyBuffer, m_proxyBuffer + m_proxyCount,
		Test::IsProxyInvalid);
	m_proxyCount = (int32) (lastProxy - m_proxyBuffer);

	// update contacts
	for (int32 k = 0; k < m_contactCount; k++)
	{
		b2ParticleContact& contact = m_contactBuffer[k];
		contact.indexA = newIndices[contact.indexA];
		contact.indexB = newIndices[contact.indexB];
	}
	b2ParticleContact* lastContact = std::remove_if(
		m_contactBuffer, m_contactBuffer + m_contactCount,
		Test::IsContactInvalid);
	m_contactCount = (int32) (lastContact - m_contactBuffer);

	// update particle-body contacts
	for (int32 k = 0; k < m_bodyContactCount; k++)
	{
		b2ParticleBodyContact& contact = m_bodyContactBuffer[k];
		contact.index = newIndices[contact.index];
	}
	b2ParticleBodyContact* lastBodyContact = std::remove_if(
		m_bodyContactBuffer, m_bodyContactBuffer + m_bodyContactCount,
		Test::IsBodyContactInvalid);
	m_bodyContactCount = (int32) (lastBodyContact - m_bodyContactBuffer);

	// update pairs
	for (int32 k = 0; k < m_pairCount; k++)
	{
		Pair& pair = m_pairBuffer[k];
		pair.indexA = newIndices[pair.indexA];
		pair.indexB = newIndices[pair.indexB];
	}
	Pair* lastPair = std::remove_if(
		m_pairBuffer, m_pairBuffer + m_pairCount, Test::IsPairInvalid);
	m_pairCount = (int32) (lastPair - m_pairBuffer);

	// update triads
	for (int32 k = 0; k < m_triadCount; k++)
	{
		Triad& triad = m_triadBuffer[k];
		triad.indexA = newIndices[triad.indexA];
		triad.indexB = newIndices[triad.indexB];
		triad.indexC = newIndices[triad.indexC];
	}
	Triad* lastTriad = std::remove_if(
		m_triadBuffer, m_triadBuffer + m_triadCount,
		Test::IsTriadInvalid);
	m_triadCount = (int32) (lastTriad - m_triadBuffer);

	// update groups
	for (b2ParticleGroup* group = m_groupList; group; group = group->GetNext())
	{
		int32 firstIndex = newCount;
		int32 lastIndex = 0;
		bool modified = false;
		for (int32 i = group->m_firstIndex; i < group->m_lastIndex; i++)
		{
			int32 j = newIndices[i];
			if (j >= 0) {
				firstIndex = b2Min(firstIndex, j);
				lastIndex = b2Max(lastIndex, j + 1);
			} else {
				modified = true;
			}
		}
		if (firstIndex < lastIndex)
		{
			group->m_firstIndex = firstIndex;
			group->m_lastIndex = lastIndex;
			if (modified)
			{
				if (group->m_groupFlags & b2_rigidParticleGroup)
				{
					group->m_toBeSplit = true;
				}
			}
		}
		else
		{
			group->m_firstIndex = 0;
			group->m_lastIndex = 0;
			if (group->m_destroyAutomatically)
			{
				group->m_toBeDestroyed = true;
			}
		}
	}

	// update particle count
	m_count = newCount;
	m_world->m_stackAllocator.Free(newIndices);

	// destroy bodies with no particles
	for (b2ParticleGroup* group = m_groupList; group;)
	{
		b2ParticleGroup* next = group->GetNext();
		if (group->m_toBeDestroyed)
		{
			DestroyParticleGroup(group);
		}
		else if (group->m_toBeSplit)
		{
			// TODO: split the group
		}
		group = next;
	}
}

void b2ParticleSystem::RotateBuffer(int32 start, int32 mid, int32 end)
{
	// move the particles assigned to the given group toward the end of array
	if (start == mid || mid == end)
	{
		return;
	}
	struct NewIndices
	{
		int32 operator[](int32 i) const
		{
			if (i < start)
			{
				return i;
			}
			else if (i < mid)
			{
				return i + end - mid;
			}
			else if (i < end)
			{
				return i + start - mid;
			}
			else
			{
				return i;
			}
		}
		int32 start, mid, end;
	} newIndices;
	newIndices.start = start;
	newIndices.mid = mid;
	newIndices.end = end;

	std::rotate(m_flagsBuffer.data + start, m_flagsBuffer.data + mid, m_flagsBuffer.data + end);
	std::rotate(m_positionBuffer.data + start, m_positionBuffer.data + mid, m_positionBuffer.data + end);
	std::rotate(m_velocityBuffer.data + start, m_velocityBuffer.data + mid, m_velocityBuffer.data + end);
	std::rotate(m_groupBuffer + start, m_groupBuffer + mid, m_groupBuffer + end);
	if (m_depthBuffer)
	{
		std::rotate(m_depthBuffer + start, m_depthBuffer + mid, m_depthBuffer + end);
	}
	if (m_colorBuffer.data)
	{
		std::rotate(m_colorBuffer.data + start, m_colorBuffer.data + mid, m_colorBuffer.data + end);
	}
	if (m_userDataBuffer.data)
	{
		std::rotate(m_userDataBuffer.data + start, m_userDataBuffer.data + mid, m_userDataBuffer.data + end);
	}

	// update proxies
	for (int32 k = 0; k < m_proxyCount; k++)
	{
		Proxy& proxy = m_proxyBuffer[k];
		proxy.index = newIndices[proxy.index];
	}

	// update contacts
	for (int32 k = 0; k < m_contactCount; k++)
	{
		b2ParticleContact& contact = m_contactBuffer[k];
		contact.indexA = newIndices[contact.indexA];
		contact.indexB = newIndices[contact.indexB];
	}

	// update particle-body contacts
	for (int32 k = 0; k < m_bodyContactCount; k++)
	{
		b2ParticleBodyContact& contact = m_bodyContactBuffer[k];
		contact.index = newIndices[contact.index];
	}

	// update pairs
	for (int32 k = 0; k < m_pairCount; k++)
	{
		Pair& pair = m_pairBuffer[k];
		pair.indexA = newIndices[pair.indexA];
		pair.indexB = newIndices[pair.indexB];
	}

	// update triads
	for (int32 k = 0; k < m_triadCount; k++)
	{
		Triad& triad = m_triadBuffer[k];
		triad.indexA = newIndices[triad.indexA];
		triad.indexB = newIndices[triad.indexB];
		triad.indexC = newIndices[triad.indexC];
	}

	// update groups
	for (b2ParticleGroup* group = m_groupList; group; group = group->GetNext())
	{
		group->m_firstIndex = newIndices[group->m_firstIndex];
		group->m_lastIndex = newIndices[group->m_lastIndex - 1] + 1;
	}
}

void b2ParticleSystem::SetParticleRadius(float32 radius)
{
	m_particleDiameter = 2 * radius;
	m_squaredDiameter = m_particleDiameter * m_particleDiameter;
	m_inverseDiameter = 1 / m_particleDiameter;
}

void b2ParticleSystem::SetParticleDensity(float32 density)
{
	m_density = density;
	m_inverseDensity =  1 / m_density;
}

float32 b2ParticleSystem::GetParticleDensity() const
{
	return m_density;
}

void b2ParticleSystem::SetParticleGravityScale(float32 gravityScale)
{
	m_gravityScale = gravityScale;
}

float32 b2ParticleSystem::GetParticleGravityScale() const
{
	return m_gravityScale;
}

void b2ParticleSystem::SetParticleDamping(float32 damping)
{
	m_dampingStrength = damping;
}

float32 b2ParticleSystem::GetParticleDamping() const
{
	return m_dampingStrength;
}

float32 b2ParticleSystem::GetParticleRadius() const
{
	return m_particleDiameter / 2;
}

float32 b2ParticleSystem::GetCriticalVelocity(const b2TimeStep& step) const
{
	return m_particleDiameter * step.inv_dt;
}

float32 b2ParticleSystem::GetCriticalVelocitySquared(const b2TimeStep& step) const
{
	float32 velocity = GetCriticalVelocity(step);
	return velocity * velocity;
}

float32 b2ParticleSystem::GetCriticalPressure(const b2TimeStep& step) const
{
	return m_density * GetCriticalVelocitySquared(step);
}

float32 b2ParticleSystem::GetParticleStride() const
{
	return b2_particleStride * m_particleDiameter;
}

float32 b2ParticleSystem::GetParticleMass() const
{
	float32 stride = GetParticleStride();
	return m_density * stride * stride;
}

float32 b2ParticleSystem::GetParticleInvMass() const
{
	return 1.777777f * m_inverseDensity * m_inverseDiameter * m_inverseDiameter;
}

uint32* b2ParticleSystem::GetParticleFlagsBuffer()
{
	return m_flagsBuffer.data;
}

float32* b2ParticleSystem::GetParticleAccumulationBuffer()
{
	return m_dataBuffer;
}

b2Vec2* b2ParticleSystem::GetParticlePositionBuffer()
{
	return m_positionBuffer.data;
}

b2Vec2* b2ParticleSystem::GetParticleVelocityBuffer()
{
	return m_velocityBuffer.data;
}

b2ParticleColor* b2ParticleSystem::GetParticleColorBuffer()
{
	m_colorBuffer.data = RequestParticleBuffer(m_colorBuffer.data);
	return m_colorBuffer.data;
}

void** b2ParticleSystem::GetParticleUserDataBuffer()
{
	m_userDataBuffer.data = RequestParticleBuffer(m_userDataBuffer.data);
	return m_userDataBuffer.data;
}

int32 b2ParticleSystem::GetParticleMaxCount() const
{
	return m_maxCount;
}

void b2ParticleSystem::SetParticleMaxCount(int32 count)
{
	b2Assert(m_count <= count);
	m_maxCount = count;
}

const uint32* b2ParticleSystem::GetParticleFlagsBuffer() const
{
	return m_flagsBuffer.data;
}

const b2Vec2* b2ParticleSystem::GetParticlePositionBuffer() const
{
	return m_positionBuffer.data;
}

const b2Vec2* b2ParticleSystem::GetParticleVelocityBuffer() const
{
	return m_velocityBuffer.data;
}

const b2ParticleColor* b2ParticleSystem::GetParticleColorBuffer() const
{
	return ((b2ParticleSystem*) this)->GetParticleColorBuffer();
}

const b2ParticleGroup* const* b2ParticleSystem::GetParticleGroupBuffer() const
{
	return m_groupBuffer;
}

void* const* b2ParticleSystem::GetParticleUserDataBuffer() const
{
	return ((b2ParticleSystem*) this)->GetParticleUserDataBuffer();
}

template <typename T> void b2ParticleSystem::SetParticleBuffer(ParticleBuffer<T>* buffer, T* newData, int32 newCapacity)
{
	b2Assert((newData && newCapacity) || (!newData && !newCapacity));
	if (!buffer->userSuppliedCapacity)
	{
		m_world->m_blockAllocator.Free(buffer->data, sizeof(T) * m_internalAllocatedCapacity);
	}
	buffer->data = newData;
	buffer->userSuppliedCapacity = newCapacity;}

void b2ParticleSystem::SetParticleFlagsBuffer(uint32* buffer, int32 capacity)
{
	SetParticleBuffer(&m_flagsBuffer, buffer, capacity);
}

void b2ParticleSystem::SetParticlePositionBuffer(b2Vec2* buffer, int32 capacity)
{
	SetParticleBuffer(&m_positionBuffer, buffer, capacity);
}

void b2ParticleSystem::SetParticleVelocityBuffer(b2Vec2* buffer, int32 capacity)
{
	SetParticleBuffer(&m_velocityBuffer, buffer, capacity);
}

void b2ParticleSystem::SetParticleColorBuffer(b2ParticleColor* buffer, int32 capacity)
{
	SetParticleBuffer(&m_colorBuffer, buffer, capacity);
}

b2ParticleGroup* const* b2ParticleSystem::GetParticleGroupBuffer()
{
	return m_groupBuffer;
}

void b2ParticleSystem::SetParticleUserDataBuffer(void** buffer, int32 capacity)
{
	SetParticleBuffer(&m_userDataBuffer, buffer, capacity);
}

void b2ParticleSystem::QueryAABB(b2QueryCallback* callback, const b2AABB& aabb) const
{
	if (m_proxyCount == 0)
	{
		return;
	}
	Proxy* beginProxy = m_proxyBuffer;
	Proxy* endProxy = beginProxy + m_proxyCount;
	Proxy* firstProxy = std::lower_bound(
		beginProxy, endProxy,
		computeTag(
			m_inverseDiameter * aabb.lowerBound.x,
			m_inverseDiameter * aabb.lowerBound.y));
	Proxy* lastProxy = std::upper_bound(
		firstProxy, endProxy,
		computeTag(
			m_inverseDiameter * aabb.upperBound.x,
			m_inverseDiameter * aabb.upperBound.y));
	for (Proxy* proxy = firstProxy; proxy < lastProxy; ++proxy)
	{
		int32 i = proxy->index;
		const b2Vec2& p = m_positionBuffer.data[i];
		if (aabb.lowerBound.x < p.x && p.x < aabb.upperBound.x &&
			aabb.lowerBound.y < p.y && p.y < aabb.upperBound.y)
		{
			if (!callback->ReportParticle(i))
			{
				break;
			}
		}
	}
}

void b2ParticleSystem::RayCast(b2RayCastCallback* callback, const b2Vec2& point1, const b2Vec2& point2) const
{
	if (m_proxyCount == 0)
	{
		return;
	}
	Proxy* beginProxy = m_proxyBuffer;
	Proxy* endProxy = beginProxy + m_proxyCount;
	Proxy* firstProxy = std::lower_bound(
		beginProxy, endProxy,
		computeTag(
			m_inverseDiameter * b2Min(point1.x, point2.x) - 1,
			m_inverseDiameter * b2Min(point1.y, point2.y) - 1));
	Proxy* lastProxy = std::upper_bound(
		firstProxy, endProxy,
		computeTag(
			m_inverseDiameter * b2Max(point1.x, point2.x) + 1,
			m_inverseDiameter * b2Max(point1.y, point2.y) + 1));
	float32 fraction = 1;
	// solving the following equation:
	// ((1-t)*point1+t*point2-position)^2=diameter^2
	// where t is a potential fraction
	b2Vec2 v = point2 - point1;
	float32 v2 = b2Dot(v, v);
	for (Proxy* proxy = firstProxy; proxy < lastProxy; ++proxy)
	{
		int32 i = proxy->index;
		b2Vec2 p = point1 - m_positionBuffer.data[i];
		float32 pv = b2Dot(p, v);
		float32 p2 = b2Dot(p, p);
		float32 determinant = pv * pv - v2 * (p2 - m_squaredDiameter);
		if (determinant >= 0)
		{
			float32 sqrtDeterminant = b2Sqrt(determinant);
			// find a solution between 0 and fraction
			float32 t = (-pv - sqrtDeterminant) / v2;
			if (t > fraction)
			{
				continue;
			}
			if (t < 0)
			{
				t = (-pv + sqrtDeterminant) / v2;
				if (t < 0 || t > fraction)
				{
					continue;
				}
			}
			b2Vec2 n = p + t * v;
			n.Normalize();
			float32 f = callback->ReportParticle(i, point1 + t * v, n, t);
			fraction = b2Min(fraction, f);
			if (fraction <= 0)
			{
				break;
			}
		}
	}
}

float32 b2ParticleSystem::ComputeParticleCollisionEnergy() const
{
	float32 sum_v2 = 0;
	for (int32 k = 0; k < m_contactCount; k++)
	{
		const b2ParticleContact& contact = m_contactBuffer[k];
		int32 a = contact.indexA;
		int32 b = contact.indexB;
		b2Vec2 n = contact.normal;
		b2Vec2 v = m_velocityBuffer.data[b] - m_velocityBuffer.data[a];
		float32 vn = b2Dot(v, n);
		if (vn < 0)
		{
			sum_v2 += vn * vn;
		}
	}
	return 0.5f * GetParticleMass() * sum_v2;
}
