void ParticleSurface::sortAndUploadIndices(shared_ptr<ParticleSurface> particleSurface, const Vector3& csz) { ParticleSystem::s_sortArray.fastClear(); shared_ptr<ParticleSystem::Block> block = particleSurface->m_block; shared_ptr<ParticleSystem> particleSystem = block->particleSystem.lock(); for (int i = 0; i < particleSystem->m_particle.size(); ++i) { const ParticleSystem::Particle& particle = particleSystem->m_particle[i]; CFrame cframe; particleSurface->getCoordinateFrame(cframe); const Point3& wsPosition = particleSystem->particlesAreInWorldSpace() ? particle.position : cframe.pointToWorldSpace(particle.position); const float particleCSZ = dot(wsPosition, csz); ParticleSystem::SortProxy proxy; proxy.index = block->startIndex + i; proxy.z = particleCSZ; ParticleSystem::s_sortArray.append(proxy); } ParticleSystem::s_sortArray.sort(SORT_DECREASING); ParticleSystem::ParticleBuffer& pBuffer = ParticleSystem::s_particleBuffer; bool needsReallocation = true; if (pBuffer.indexStream.valid() && (pBuffer.indexStream.maxSize() >= size_t(ParticleSystem::s_sortArray.size()))) { needsReallocation = false; } if (needsReallocation) { int numToAllocate = ParticleSystem::s_sortArray.size() * 2; shared_ptr<VertexBuffer> vb = VertexBuffer::create(sizeof(int) * numToAllocate + 8); int ignored; pBuffer.indexStream = IndexStream(ignored, numToAllocate, vb); } static Array<int> sortedIndices; sortedIndices.fastClear(); for (const ParticleSystem::SortProxy& p : ParticleSystem::s_sortArray) { sortedIndices.append(p.index); } pBuffer.indexStream.update(sortedIndices); }
DirectionHistogram::DirectionHistogram(int numSlices, const Vector3& axis) : m_slices(numSlices) { alwaysAssertM(numSlices >= 4, "At least four slices required"); // Turn on old hemisphere-only optimization const bool hemi = true; // Normalize const Vector3& Z = axis.direction(); Vector3 X = (abs(Z.dot(Vector3::unitX())) <= 0.9f) ? Vector3::unitX() : Vector3::unitY(); X = (X - Z * (Z.dot(X))).direction(); const Vector3 Y = Z.cross(X); // Only generate upper hemisphere static const int P = m_slices; static const int T = hemi ? (m_slices / 2) : m_slices; const float thetaExtent = float( hemi ? G3D::halfPi() : G3D::pi() ); for (int t = 0; t < T; ++t) { const float theta = t * thetaExtent / (T - 1.0f); const float z = cos(theta); const float r = sin(theta); const bool firstRow = (t == 0); const bool secondRow = (t == 1); const bool lastRow = ! hemi && (t == T - 1); for (int p = 0; p < P; ++p) { const float phi = p * float(G3D::twoPi()) / P; const float x = cos(phi) * r; const float y = sin(phi) * r; const bool unique = (! firstRow && ! lastRow) || (p == 0); // Only insert one vertex at each pole if (unique) { m_meshVertex.append(X * x + Y * y + Z * z); } const int i = m_meshVertex.size() - 1; // Index of the start of this row const int rowStart = ((i - 1) / P) * P + 1; const int colOffset = i - rowStart; if (firstRow) { // (First row generates no quads) } else if (secondRow) { // Degnererate north pole m_meshIndex.append(0, 0, i, rowStart + (colOffset + 1) % P); } else if (lastRow) { // Degenerate south pole m_meshIndex.append(i, i, i - p - 1, i - p - 2); } else { m_meshIndex.append( i - P, i, rowStart + (colOffset + 1) % P, rowStart + ((colOffset + 1) % P) - P); } } } m_bucket.resize(m_meshVertex.size()); reset(); // We initially accumulate areas and then invert them in a follow-up pass m_invArea.resize(m_meshIndex.size()); // Zero the array System::memset(m_invArea.getCArray(), 0, sizeof(float) * m_invArea.size()); CPUVertexArray vertexArray; // Create triTree { vertexArray.hasTangent = false; vertexArray.hasTexCoord0 = false; for(int i = 0; i < m_meshVertex.size(); ++i){ CPUVertexArray::Vertex v; v.position = m_meshVertex[i]; v.normal = m_meshVertex[i]; vertexArray.vertex.append(v); } Array<Tri> triArray; for (int q = 0; q < m_meshIndex.size(); q += 4) { const int i0 = m_meshIndex[q]; const int i1 = m_meshIndex[q + 1]; const int i2 = m_meshIndex[q + 2]; const int i3 = m_meshIndex[q + 3]; // Create two tris for each quad // Wind backwards; these tris have to face inward const Proxy<Material>::Ref vii(VertexIndexIndex::create(q)); Tri A(i0, i3, i2, vertexArray, vii); Tri B(i0, i2, i1, vertexArray, vii); triArray.append(A); triArray.append(B); // Attribute the area of the surrounding quads to each vertex. If we don't do this, then // vertices near the equator will recieve only half of the correct probability. float area = A.area() + B.area(); m_invArea[i0] += area; m_invArea[i1] += area; m_invArea[i2] += area; m_invArea[i3] += area; } m_tree.setContents(triArray, vertexArray); //ASKMORGAN vertexArray.vertex.clear(); for (int i = 0; i < m_invArea.size(); ++i) { // Multiply by a small number to keep these from getting too large m_invArea[i] = 0.001f / m_invArea[i]; } } shared_ptr<VertexBuffer> dataArea = VertexBuffer::create(sizeof(Vector3) * m_meshVertex.size(), VertexBuffer::WRITE_EVERY_FEW_FRAMES); m_gpuMeshVertex = AttributeArray(m_meshVertex, dataArea); shared_ptr<VertexBuffer> indexArea = VertexBuffer::create(sizeof(int) * m_meshIndex.size(), VertexBuffer::WRITE_ONCE); m_gpuMeshIndex = IndexStream(m_meshIndex, indexArea); m_dirty = false; }
void MD2Model::Part::load(const std::string& filename, float resize) { resize *= 0.55f; // If models are being reloaded it is dangerous to trust the interpolation cache. interpolatedModel = NULL; alwaysAssertM(FileSystem::exists(filename), std::string("Can't find \"") + filename + "\""); setNormalTable(); // Clear out reset(); BinaryInput b(filename, G3D_LITTLE_ENDIAN); MD2ModelHeader header; header.deserialize(b); debugAssert(header.version == 8); debugAssert(header.numVertices <= 4096); keyFrame.resize(header.numFrames); Array<Vector3> frameMin; frameMin.resize(header.numFrames); Array<Vector3> frameMax; frameMax.resize(header.numFrames); Array<double> frameRad; frameRad.resize(header.numFrames); texCoordScale.x = 1.0f / header.skinWidth; texCoordScale.y = 1.0f / header.skinHeight; Vector3 min = Vector3::inf(); Vector3 max = -Vector3::inf(); double rad = 0; if (header.numVertices < 3) { Log::common()->printf("\n*****************\nWarning: \"%s\" is corrupted and is not being loaded.\n", filename.c_str()); return; } loadTextureFilenames(b, header.numSkins, header.offsetSkins); for (int f = 0; f < keyFrame.size(); ++f) { MD2Frame md2Frame; b.setPosition(header.offsetFrames + f * header.frameSize); md2Frame.deserialize(b); // Read the vertices for the frame keyFrame[f].vertexArray.resize(header.numVertices); keyFrame[f].normalArray.resize(header.numVertices); // Per-pose bounds Vector3 min_1 = Vector3::inf(); Vector3 max_1 = -Vector3::inf(); double rad_1 = 0; // Quake's axes are permuted and scaled double scale[3] = {-.07, .07, -.07}; int permute[3] = {2, 0, 1}; int v, i; for (v = 0; v < header.numVertices; ++v) { Vector3& vertex = keyFrame[f].vertexArray[v]; for (i = 0; i < 3; ++i) { vertex[permute[i]] = (b.readUInt8() * md2Frame.scale[i] + md2Frame.translate[i]) * float(scale[permute[i]]); } vertex *= resize; uint8 normalIndex = b.readUInt8(); debugAssertM(normalIndex < 162, "Illegal canonical normal index in file"); keyFrame[f].normalArray[v] = iClamp(normalIndex, 0, 161); min_1 = min_1.min(vertex); max_1 = max_1.max(vertex); if (vertex.squaredMagnitude() > rad_1) { rad_1 = vertex.squaredMagnitude(); } } frameMin[f] = min_1; frameMax[f] = max_1; frameRad[f] = sqrt(rad_1); min = min.min(min_1); max = max.max(max_1); if (rad_1 > rad) { rad = rad_1; } } // Compute per-animation bounds based on frame bounds for (int a = 0; a < JUMP; ++a) { const int first = animationTable[a].first; const int last = animationTable[a].last; if ((first < header.numFrames) && (last < header.numFrames)) { Vector3 min = frameMin[first]; Vector3 max = frameMax[first]; double rad = frameRad[first]; for (int i = first + 1; i <= last; ++i) { min = min.min(frameMin[i]); max = max.max(frameMax[i]); rad = G3D::max(rad, frameRad[i]); } animationBoundingBox[a] = AABox(min, max); // Sometimes the sphere bounding the box is tighter than the one we calculated. const float boxRadSq = (max-min).squaredMagnitude() * 0.25f; if (boxRadSq >= square(rad)) { animationBoundingSphere[a] = Sphere(Vector3::zero(), (float)rad); } else { animationBoundingSphere[a] = Sphere((max + min) * 0.5f, sqrt(boxRadSq)); } } else { // This animation is not supported by this model animationBoundingBox[a] = AABox(Vector3::zero(), Vector3::zero()); animationBoundingSphere[a] = Sphere(Vector3::zero(), 0); } } animationBoundingBox[JUMP] = animationBoundingBox[JUMP_DOWN]; animationBoundingSphere[JUMP] = animationBoundingSphere[JUMP_DOWN]; boundingBox = AABox(min, max); boundingSphere = Sphere(Vector3::zero(), (float)sqrt(rad)); // Load the texture coords Array<Vector2int16> fileTexCoords; fileTexCoords.resize(header.numTexCoords); b.setPosition(header.offsetTexCoords); for (int t = 0; t < fileTexCoords.size(); ++t) { fileTexCoords[t].x = b.readUInt16(); fileTexCoords[t].y = b.readUInt16(); } // The indices for the texture coords (which don't match the // vertex indices originally). indexArray.resize(header.numTriangles * 3); Array<Vector2int16> index_texCoordArray; index_texCoordArray.resize(indexArray.size()); // Read the triangles, reversing them to get triangle list order b.setPosition(header.offsetTriangles); for (int t = header.numTriangles - 1; t >= 0; --t) { for (int i = 2; i >= 0; --i) { indexArray[t * 3 + i] = b.readUInt16(); } for (int i = 2; i >= 0; --i) { index_texCoordArray[t * 3 + i] = fileTexCoords[b.readUInt16()]; } } computeTexCoords(index_texCoordArray); // Read the primitives { primitiveArray.clear(); b.setPosition(header.offsetGlCommands); int n = b.readInt32(); while (n != 0) { Primitive& primitive = primitiveArray.next(); if (n > 0) { primitive.type = PrimitiveType::TRIANGLE_STRIP; } else { primitive.type = PrimitiveType::TRIANGLE_FAN; n = -n; } primitive.pvertexArray.resize(n); Array<Primitive::PVertex>& pvertex = primitive.pvertexArray; for (int i = 0; i < pvertex.size(); ++i) { pvertex[i].texCoord.x = b.readFloat32(); pvertex[i].texCoord.y = b.readFloat32(); pvertex[i].index = b.readInt32(); } n = b.readInt32(); } } MeshAlg::computeAdjacency(keyFrame[0].vertexArray, indexArray, faceArray, edgeArray, vertexArray); weldedFaceArray = faceArray; weldedEdgeArray = edgeArray; weldedVertexArray = vertexArray; MeshAlg::weldAdjacency(keyFrame[0].vertexArray, weldedFaceArray, weldedEdgeArray, weldedVertexArray); numBoundaryEdges = MeshAlg::countBoundaryEdges(edgeArray); numWeldedBoundaryEdges = MeshAlg::countBoundaryEdges(weldedEdgeArray); shared_ptr<VertexBuffer> indexBuffer = VertexBuffer::create(indexArray.size() * sizeof(int), VertexBuffer::WRITE_ONCE); indexVAR = IndexStream(indexArray, indexBuffer); }