summaryrefslogtreecommitdiff
path: root/Runtime/Graphics/ParticleSystem/Modules/ShapeModule.cpp
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2019-08-14 22:50:43 +0800
committerchai <chaifix@163.com>2019-08-14 22:50:43 +0800
commit15740faf9fe9fe4be08965098bbf2947e096aeeb (patch)
treea730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/Graphics/ParticleSystem/Modules/ShapeModule.cpp
+Unity Runtime codeHEADmaster
Diffstat (limited to 'Runtime/Graphics/ParticleSystem/Modules/ShapeModule.cpp')
-rw-r--r--Runtime/Graphics/ParticleSystem/Modules/ShapeModule.cpp650
1 files changed, 650 insertions, 0 deletions
diff --git a/Runtime/Graphics/ParticleSystem/Modules/ShapeModule.cpp b/Runtime/Graphics/ParticleSystem/Modules/ShapeModule.cpp
new file mode 100644
index 0000000..3cdb007
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/Modules/ShapeModule.cpp
@@ -0,0 +1,650 @@
+#include "UnityPrefix.h"
+#include "ShapeModule.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystemUtils.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystem.h"
+#include "Runtime/Graphics/TriStripper.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Random/Random.h"
+#include "Runtime/Geometry/ComputionalGeometry.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Utilities/StrideIterator.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+
+enum MeshDistributionMode
+{
+ kDistributionVertex,
+ kDistributionTriangle,
+};
+
+
+/// This gives a random barycentric coord (on the edge of triangle)
+// @TODO: Stupid: Make this in a faster way
+inline Vector3f RandomBarycentricCoordEdge (Rand& rand)
+{
+ float u = rand.GetFloat ();
+ float v = rand.GetFloat ();
+ if (u + v > 1.0F)
+ {
+ u = 1.0F - u;
+ v = 1.0F - v;
+ }
+ float w = 1.0F - u - v;
+
+ int edge = RangedRandom(rand, 0, 2);
+ if(0 == edge)
+ {
+ v += 0.5f * u;
+ w += 0.5f * u;
+ u = 0.0f;
+ }
+ else if(1 == edge)
+ {
+ u += 0.5f * v;
+ w += 0.5f * v;
+ v = 0.0f;
+ }
+ else
+ {
+ u += 0.5f * w;
+ v += 0.5f * w;
+ w = 0.0f;
+ }
+
+ return Vector3f (u, v, w);
+}
+
+
+// TODO: It could make sense to initialize in separated loops. i.e. separate position and velcoity vectors
+inline void EmitterStoreData(const Matrix4x4f& localToWorld, const Vector3f& scale, ParticleSystemParticles& ps, size_t q, Vector3f& pos, Vector3f& n, Rand& random, bool randomDirection)
+{
+ if(randomDirection)
+ n = RandomUnitVector (random);
+
+ n = NormalizeSafe(n);
+
+ pos = Scale(pos, scale);
+
+ Vector3f vel = Magnitude (ps.velocity[q]) * n;
+ vel = localToWorld.MultiplyVector3 (vel);
+
+ // @TODO: Sooo... why multiply point and then undo the result of it? Why not just MultiplyVector?
+ pos = localToWorld.MultiplyPoint3 (pos) - localToWorld.GetPosition();
+ ps.position[q] += pos;
+ ps.velocity[q] = vel;
+
+#if 0 // WIP code for converting to spherical
+ Vector3f sp = ps.position[q];
+ ps.position[q].x = Sqrt(sp.x*sp.x + sp.y*sp.y + sp.z*sp.z);
+ ps.position[q].y = acosf(sp.z/ps.position[q].x);
+ ps.position[q].z = acosf(sp.y/ps.position[q].x);
+#endif
+
+ if(ps.usesAxisOfRotation)
+ {
+ Vector3f tan = Cross (-n, Vector3f::zAxis);
+ if (SqrMagnitude (tan) <= 0.01)
+ tan = Cross (-pos, Vector3f::zAxis);
+ if (SqrMagnitude (tan) <= 0.01)
+ tan = Vector3f::yAxis;
+ ps.axisOfRotation[q] = Normalize (tan);
+ }
+}
+
+inline void EmitterStoreData(const Matrix4x4f& localToWorld, const Vector3f& scale, ParticleSystemParticles& ps, size_t q, Vector3f& pos, Vector3f& n, ColorRGBA32& color, Rand& random, bool randomDirection)
+{
+ EmitterStoreData(localToWorld, scale, ps, q, pos, n, random, randomDirection);
+ ps.color[q] *= color;
+}
+
+
+template<MeshDistributionMode distributionMode>
+void GetPositionMesh (Vector3f& pos,
+ Vector3f& n,
+ ColorRGBA32& color,
+ const ParticleSystemEmitterMeshVertex* vertexData,
+ const int vertexCount,
+ const MeshTriangleData* triangleData,
+ const UInt32 numPrimitives,
+ float totalTriangleArea,
+ Rand& random,
+ bool edge)
+{
+ // position/normal of particle is vertex/vertex normal from mesh
+ if(kDistributionVertex == distributionMode)
+ {
+ int vertexIndex = RangedRandom (random, 0, vertexCount);
+ pos = vertexData[vertexIndex].position;
+ n = vertexData[vertexIndex].normal;
+ color = vertexData[vertexIndex].color;
+ }
+ else if(kDistributionTriangle == distributionMode)
+ {
+ float randomArea = RangedRandom(random, 0.0f, totalTriangleArea);
+ float accArea = 0.0f;
+ UInt32 triangleIndex = 0;
+
+ for(UInt32 i = 0; i < numPrimitives; i++)
+ {
+ const MeshTriangleData& data = triangleData[i];
+ accArea += data.area;
+ if(accArea >= randomArea)
+ {
+ triangleIndex = i;
+ break;
+ }
+ }
+
+ const MeshTriangleData& data = triangleData[triangleIndex];
+ UInt16 a = data.indices[0];
+ UInt16 b = data.indices[1];
+ UInt16 c = data.indices[2];
+
+ Vector3f barycenter;
+ if(edge)
+ barycenter = RandomBarycentricCoordEdge (random);
+ else
+ barycenter = RandomBarycentricCoord (random);
+
+ // Interpolate vertex with barycentric coordinate
+ pos = barycenter.x * vertexData[a].position + barycenter.y * vertexData[b].position + barycenter.z * vertexData[c].position;
+ n = barycenter.x * vertexData[a].normal + barycenter.y * vertexData[b].normal + barycenter.z * vertexData[c].normal;
+
+ // TODO: Don't convert to floats!!!
+ ColorRGBAf color1 = vertexData[a].color;
+ ColorRGBAf color2 = vertexData[b].color;
+ ColorRGBAf color3 = vertexData[c].color;
+ color = barycenter.x * color1 + barycenter.y * color2 + barycenter.z * color3;
+ }
+}
+
+static bool CompareMeshTriangleData (const MeshTriangleData& a, const MeshTriangleData& b)
+{
+ return (a.area > b.area);
+}
+
+static float BuildMeshAreaTable(MeshTriangleData* triData, const StrideIterator<Vector3f> vertices, const UInt16* indices, int numTriangles)
+{
+ float result = 0.0f;
+ for(int i = 0; i < numTriangles; i++)
+ {
+ const UInt16 a = indices[i * 3 + 0];
+ const UInt16 b = indices[i * 3 + 1];
+ const UInt16 c = indices[i * 3 + 2];
+ float area = TriangleArea3D (vertices[a], vertices[b], vertices[c]);
+ result += area;
+
+ triData[i].indices[0] = a;
+ triData[i].indices[1] = b;
+ triData[i].indices[2] = c;
+ triData[i].area = area;
+ }
+
+ return result;
+}
+
+// ------------------------------------------------------------------------------------------
+
+ShapeModule::ShapeModule () : ParticleSystemModule(true)
+, m_Type (kCone)
+, m_RandomDirection (false)
+, m_Angle(25.0f)
+, m_Radius(1.0f)
+, m_Length(5.0f)
+, m_BoxX(1.0f)
+, m_BoxY(1.0f)
+, m_BoxZ(1.0f)
+, m_PlacementMode(kVertex)
+, m_CachedMesh(NULL)
+, m_MeshNode (NULL)
+{
+}
+
+void ShapeModule::Start (const ParticleSystemReadOnlyState& roState, const ParticleSystemState& state, ParticleSystemParticles& ps, const Matrix4x4f& matrix, size_t fromIndex, float t)
+{
+ DebugAssert(roState.lengthInSec > 0.0001f);
+ const float normalizedT = t / roState.lengthInSec;
+ DebugAssert (normalizedT >= 0.0f);
+ DebugAssert (normalizedT <= 1.0f);
+
+ Rand& random = GetRandom();
+
+ if (m_Type == kMesh)
+ {
+ if(!m_CachedMesh)
+ return;
+
+ if(!m_CachedVertexData.size())
+ return;
+
+ if(!m_CachedTriangleData.size())
+ return;
+
+ const ParticleSystemEmitterMeshVertex* vertexData = &m_CachedVertexData[0];
+ const int vertexCount = m_CachedVertexData.size();
+ size_t count = ps.array_size ();
+ switch(m_PlacementMode)
+ {
+ case kVertex:
+ {
+ for (int q = fromIndex; q < count; ++q)
+ {
+ Vector3f pos;
+ Vector3f n;
+ ColorRGBA32 color;
+ GetPositionMesh<kDistributionVertex>(pos, n, color, vertexData, vertexCount, NULL, 0, m_CachedTotalTriangleArea, random, false);
+ EmitterStoreData(matrix, state.emitterScale, ps, q, pos, n, color, random, m_RandomDirection);
+ }
+ break;
+ }
+ case kEdge:
+ {
+ for (int q = fromIndex; q < count; ++q)
+ {
+ Vector3f pos;
+ Vector3f n;
+ ColorRGBA32 color;
+ GetPositionMesh<kDistributionTriangle>(pos, n, color, vertexData, vertexCount, m_CachedTriangleData.begin(), m_CachedTriangleData.size(), m_CachedTotalTriangleArea, random, true);
+ EmitterStoreData(matrix, state.emitterScale, ps, q, pos, n, color, random, m_RandomDirection);
+ }
+ break;
+ }
+ case kTriangle:
+ {
+ for (int q = fromIndex; q < count; ++q)
+ {
+ Vector3f pos;
+ Vector3f n;
+ ColorRGBA32 color;
+ GetPositionMesh<kDistributionTriangle>(pos, n, color, vertexData, vertexCount, m_CachedTriangleData.begin(), m_CachedTriangleData.size(), m_CachedTotalTriangleArea, random, false);
+ EmitterStoreData(matrix, state.emitterScale, ps, q, pos, n, color, random, m_RandomDirection);
+ }
+ break;
+ }
+ default:
+ {
+ DebugAssert(0 && "PlacementMode Not Supported");
+ }
+ }
+ }
+ else
+ {
+ const float r = m_Radius;
+
+ float a = Deg2Rad (m_Angle);
+ float sinA = Sin (a);
+ float cosA = Cos (a);
+ float length = m_Length;
+
+ const size_t count = ps.array_size ();
+ switch(m_Type)
+ {
+ case kSphere:
+ {
+ for (int q = fromIndex; q < count; ++q)
+ {
+ Vector3f pos = RandomPointInsideUnitSphere (random) * r;
+ Vector3f n = pos;
+ EmitterStoreData(matrix, state.emitterScale, ps, q, pos, n, random, m_RandomDirection);
+ }
+ break;
+ }
+ case kSphereShell:
+ {
+ for (int q = fromIndex; q < count; ++q)
+ {
+ Vector3f pos = RandomUnitVector(random) * r;
+ Vector3f n = pos;
+ EmitterStoreData(matrix, state.emitterScale, ps, q, pos, n, random, m_RandomDirection);
+ }
+ break;
+ }
+ case kHemiSphere:
+ {
+ for (int q = fromIndex; q < count; ++q)
+ {
+ Vector3f pos = RandomPointInsideUnitSphere (random) * r;
+ if (pos.z < 0.0f)
+ pos.z *= -1.0f;
+ Vector3f n = pos;
+ EmitterStoreData(matrix, state.emitterScale, ps, q, pos, n, random, m_RandomDirection);
+ }
+ break;
+ }
+ case kHemiSphereShell:
+ {
+ for (int q = fromIndex; q < count; ++q)
+ {
+ Vector3f pos = RandomUnitVector (random) * r;
+ if (pos.z < 0.0f)
+ pos.z *= -1.0f;
+ Vector3f n = pos;
+ EmitterStoreData(matrix, state.emitterScale, ps, q, pos, n, random, m_RandomDirection);
+ }
+ break;
+ }
+ case kCone:
+ {
+ for (int q = fromIndex; q < count; ++q)
+ {
+ Vector2f posXY = RandomPointInsideUnitCircle (random);
+ Vector2f nXY;
+ if(m_RandomDirection)
+ nXY = RandomPointInsideUnitCircle (random) * sinA;
+ else
+ nXY = Vector2f(posXY.x, posXY.y)* sinA;
+ Vector3f n (nXY.x, nXY.y, cosA);
+ Vector3f pos (posXY.x * r, posXY.y * r, 0.0f);
+ EmitterStoreData(matrix, state.emitterScale, ps, q, pos, n, random, false);
+ }
+ break;
+ }
+ case kConeShell:
+ {
+ for (int q = fromIndex; q < count; ++q)
+ {
+ Vector2f posXY = NormalizeSafe(RandomUnitVector2 (random));
+
+ Vector2f nXY;
+ if(m_RandomDirection)
+ nXY = RandomPointInsideUnitCircle (random) * sinA;
+ else
+ nXY = Vector2f(posXY.x, posXY.y)* sinA;
+ Vector3f n (nXY.x, nXY.y, cosA);
+ Vector3f pos (posXY.x * r, posXY.y * r, 0.0f);
+ EmitterStoreData(matrix, state.emitterScale, ps, q, pos, n, random, false);
+ }
+ break;
+ }
+ case kConeVolume:
+ {
+ for (int q = fromIndex; q < count; ++q)
+ {
+ Vector2f posXY = RandomPointInsideUnitCircle (random);
+ Vector2f nXY = Vector2f(posXY.x, posXY.y)* sinA;
+ Vector3f n (nXY.x, nXY.y, cosA);
+ Vector3f pos (posXY.x * r, posXY.y * r, 0.0f);
+ pos += length * Random01(random) * NormalizeSafe(n);
+ EmitterStoreData(matrix, state.emitterScale, ps, q, pos, n, random, m_RandomDirection);
+ }
+ break;
+ }
+ case kConeVolumeShell:
+ {
+ for (int q = fromIndex; q < count; ++q)
+ {
+ Vector2f posXY = NormalizeSafe(RandomUnitVector2 (random));
+ Vector2f nXY = Vector2f(posXY.x, posXY.y)* sinA;
+ Vector3f n (nXY.x, nXY.y, cosA);
+ Vector3f pos = Vector3f(posXY.x * r, posXY.y * r, 0.0f);
+ pos += length * Random01(random) * NormalizeSafe(n);
+ EmitterStoreData(matrix, state.emitterScale, ps, q, pos, n, random, m_RandomDirection);
+ }
+ break;
+ }
+ case kBox:
+ {
+ const Vector3f extents (0.5f * m_BoxX, 0.5f * m_BoxY, 0.5f * m_BoxZ);
+ for (int q = fromIndex; q < count; ++q)
+ {
+ Vector3f pos = RandomPointInsideCube (random, extents);
+ Vector3f n = Vector3f::zAxis;
+ EmitterStoreData(matrix, state.emitterScale, ps, q, pos, n, random, m_RandomDirection);
+ }
+ }
+ break;
+ default:
+ {
+ DebugAssert(0 && "Shape not supported");
+ }
+ }
+ }
+}
+
+void ShapeModule::CalculateProceduralBounds(MinMaxAABB& bounds, const Vector3f& emitterScale, Vector2f minMaxBounds) const
+{
+ DebugAssert(minMaxBounds.x <= minMaxBounds.y);
+
+ switch(m_Type)
+ {
+ case kSphere:
+ case kSphereShell:
+ bounds.m_Max = Vector3f(m_Radius, m_Radius, m_Radius);
+ bounds.m_Min = -bounds.m_Max;
+ break;
+ case kHemiSphere:
+ case kHemiSphereShell:
+ bounds.m_Max = Vector3f(m_Radius, m_Radius, m_Radius);
+ bounds.m_Min = Vector3f(-m_Radius, -m_Radius, 0.0f);
+ break;
+ case kCone:
+ case kConeShell:
+ bounds.m_Max = Vector3f(m_Radius, m_Radius, 0.0f);
+ bounds.m_Min = -bounds.m_Max;
+ break;
+ case kConeVolume:
+ case kConeVolumeShell:
+ {
+ const float a = Deg2Rad (m_Angle);
+ const float coneRadius2 = m_Radius + m_Length * Sin (a);
+ const float coneLength = m_Length * Cos (a);
+ bounds.m_Max = Vector3f(coneRadius2, coneRadius2, coneLength);
+ bounds.m_Min = -Vector3f(coneRadius2, coneRadius2, 0.0f);
+ break;
+ }
+ case kBox:
+ bounds.m_Max = Vector3f(m_BoxX, m_BoxY, m_BoxZ) * 0.5f;
+ bounds.m_Min = -bounds.m_Max;
+ break;
+ case kMesh:
+ {
+ if(m_CachedMesh)
+ bounds = m_CachedMesh->GetBounds(0);
+ else
+ bounds = MinMaxAABB(Vector3f::zero, Vector3f::zero);
+ break;
+ }
+ default:
+ {
+ AssertBreak(!"Shape not implemented.");
+ }
+ }
+
+ bounds.m_Min = Scale(bounds.m_Min, emitterScale);
+ bounds.m_Max = Scale(bounds.m_Max, emitterScale);
+
+ MinMaxAABB speedBounds;
+
+ // Cone and cone shell random direction only deviate inside the bound
+ if(m_RandomDirection && (m_Type != kCone) && (m_Type != kConeShell))
+ {
+ speedBounds.m_Max = Vector3f::one;
+ speedBounds.m_Min = -Vector3f::one;
+ minMaxBounds = Abs(minMaxBounds);
+ }
+ else
+ {
+ switch(m_Type)
+ {
+ case kSphere:
+ case kSphereShell:
+ case kMesh:
+ speedBounds.m_Max = Vector3f::one;
+ speedBounds.m_Min = -Vector3f::one;
+ break;
+ case kHemiSphere:
+ case kHemiSphereShell:
+ speedBounds.m_Max = Vector3f::one;
+ speedBounds.m_Min = Vector3f(-1.0f, -1.0f, 0.0f);
+ break;
+ case kCone:
+ case kConeShell:
+ case kConeVolume:
+ case kConeVolumeShell:
+ {
+ const float a = Deg2Rad (m_Angle);
+ const float sinA = Sin (a);
+ speedBounds.m_Max = Vector3f(sinA, sinA, 1.0f);
+ speedBounds.m_Min = Vector3f(-sinA, -sinA, 0.0f);
+ break;
+ }
+ case kBox:
+ speedBounds.m_Max = Vector3f::zAxis;
+ speedBounds.m_Min = Vector3f::zero;
+ break;
+ default:
+ {
+ AssertBreak(!"Shape not implemented.");
+ }
+ }
+ }
+
+ MinMaxAABB speedBound;
+ speedBound.m_Min = bounds.m_Min + speedBounds.m_Min * minMaxBounds.y;
+ speedBound.m_Max = bounds.m_Max + speedBounds.m_Max * minMaxBounds.y;
+ bounds.Encapsulate(speedBound);
+
+ MinMaxAABB negSpeedBound;
+ negSpeedBound.m_Min = speedBounds.m_Min * minMaxBounds.x;
+ negSpeedBound.m_Max = speedBounds.m_Max * minMaxBounds.x;
+ speedBound.m_Min = min(negSpeedBound.m_Min, negSpeedBound.m_Max);
+ speedBound.m_Max = max(negSpeedBound.m_Min, negSpeedBound.m_Max);
+ bounds.Encapsulate(speedBound);
+}
+
+void ShapeModule::CheckConsistency ()
+{
+ m_Type = clamp<int> (m_Type, kSphere, kMax-1);
+ m_PlacementMode = clamp<int> (m_PlacementMode, kVertex, kModeMax-1);
+
+ m_Angle = clamp(m_Angle, 0.0f, 90.0f);
+ m_Radius = max(0.01f, m_Radius);
+ m_Length = max(0.0f, m_Length);
+ m_BoxX = max(0.0f, m_BoxX);
+ m_BoxY = max(0.0f, m_BoxY);
+ m_BoxZ = max(0.0f, m_BoxZ);
+}
+
+void ShapeModule::AwakeFromLoad (ParticleSystem* system, const ParticleSystemReadOnlyState& roState)
+{
+ m_MeshNode.RemoveFromList();
+ m_MeshNode.SetData(system);
+ m_CachedMesh = m_Mesh;
+ if (m_CachedMesh != NULL)
+ m_CachedMesh->AddObjectUser( m_MeshNode );
+ DidModifyMeshData();
+
+ ResetSeed(roState);
+}
+
+void ShapeModule::ResetSeed(const ParticleSystemReadOnlyState& roState)
+{
+ if(roState.randomSeed == 0)
+ m_Random.SetSeed(GetGlobalRandomSeed ());
+ else
+ m_Random.SetSeed(roState.randomSeed);
+}
+
+void ShapeModule::DidDeleteMesh (ParticleSystem* system)
+{
+ m_CachedMesh = NULL;
+}
+
+void ShapeModule::DidModifyMeshData ()
+{
+ if (m_CachedMesh == NULL)
+ {
+ m_CachedTriangleData.resize_uninitialized(0);
+ m_CachedVertexData.resize_uninitialized(0);
+ m_CachedTotalTriangleArea = 0;
+ return;
+ }
+
+
+ const StrideIterator<Vector3f> vertexBuffer = m_CachedMesh->GetVertexBegin();
+ const UInt16* indexBuffer = m_CachedMesh->GetSubMeshBuffer16(0);
+ const SubMesh& submesh = m_CachedMesh->GetSubMeshFast (0);
+ if (submesh.topology == kPrimitiveTriangleStripDeprecated)
+ {
+ const int numTriangles = CountTrianglesInStrip(indexBuffer, submesh.indexCount);
+ const int capacity = numTriangles * 3;
+ UNITY_TEMP_VECTOR(UInt16) tempIndices(capacity);
+ Destripify(indexBuffer, submesh.indexCount, &tempIndices[0], capacity);
+ m_CachedTriangleData.resize_uninitialized(numTriangles);
+ m_CachedTotalTriangleArea = BuildMeshAreaTable(m_CachedTriangleData.begin(), vertexBuffer, &tempIndices[0], numTriangles);
+ }
+ else if (submesh.topology == kPrimitiveTriangles)
+ {
+ const int numTriangles = submesh.indexCount/3;
+ m_CachedTriangleData.resize_uninitialized(numTriangles);
+ m_CachedTotalTriangleArea = BuildMeshAreaTable(m_CachedTriangleData.begin(), vertexBuffer, indexBuffer, numTriangles);
+ }
+ else
+ {
+ m_CachedMesh = NULL;
+ }
+
+ // Optimization: This sorts so big triangles comes before small, which means finding the right triangle is faster
+ std::sort(m_CachedTriangleData.begin(), m_CachedTriangleData.begin() + m_CachedTriangleData.size(), CompareMeshTriangleData);
+
+ // Cache vertices
+ const int vertexCount = m_CachedMesh->GetVertexCount();
+ const StrideIterator<Vector3f> vertices = m_CachedMesh->GetVertexBegin();
+ const StrideIterator<Vector3f> normals = m_CachedMesh->GetNormalBegin();
+ const StrideIterator<ColorRGBA32> colors = m_CachedMesh->GetColorBegin();
+ m_CachedVertexData.resize_uninitialized(vertexCount);
+ for(int i = 0; i < vertexCount; i++)
+ {
+ m_CachedVertexData[i].position = vertices[i];
+
+ if(!normals.IsNull())
+ m_CachedVertexData[i].normal = normals[i];
+ else
+ m_CachedVertexData[i].normal = Vector3f::zero;
+
+ if(!colors.IsNull())
+ m_CachedVertexData[i].color = colors[i];
+ else
+ m_CachedVertexData[i].color = ColorRGBA32(0xffffffff);
+ }
+}
+
+Rand& ShapeModule::GetRandom()
+{
+#if UNITY_EDITOR
+ if(!IsWorldPlaying())
+ return m_EditorRandom;
+ else
+#endif
+ return m_Random;
+}
+
+template<class TransferFunction>
+void ShapeModule::Transfer (TransferFunction& transfer)
+{
+ transfer.SetVersion(2);
+ ParticleSystemModule::Transfer (transfer);
+ transfer.Transfer (m_Type, "type");
+
+ // Primitive
+ transfer.Transfer(m_Radius, "radius");
+ transfer.Transfer(m_Angle, "angle");
+ transfer.Transfer(m_Length, "length");
+ transfer.Transfer(m_BoxX, "boxX");
+ transfer.Transfer(m_BoxY, "boxY");
+ transfer.Transfer(m_BoxZ, "boxZ");
+
+ // Mesh
+ transfer.Transfer (m_PlacementMode, "placementMode");
+ TRANSFER (m_Mesh);
+
+ transfer.Transfer (m_RandomDirection, "randomDirection"); transfer.Align();
+
+ // In Unity 3.5 all cone emitters had random direction set to false, but behaved as if it was true
+ if(transfer.IsOldVersion(1))
+ if(kCone == m_Type)
+ m_RandomDirection = true;
+}
+
+INSTANTIATE_TEMPLATE_TRANSFER(ShapeModule)
+