コード例 #1
0
        // Return the maximum buffer size (in bytes) required to process the
        // specified collection of market 'indices'.
//..
// Before showing the implementation of 'processIndices', where the most
// interesting use of our managed allocator takes place, we show the site of
// the call to 'processIndices'.
//
// First, assume that we have been given an 'IndexCollection' that has been
// populated with one or more 'IndexAttributes':
//..
//  IndexCollection indices;  // assume populated
//..
// Next, we calculate the size of the buffer that is needed, allocate the
// memory for the buffer from the default allocator, create our concrete
// managed allocator (namely, an instance of 'my_BufferAllocator'), and call
// 'processIndices':
//..
//  const int bufferSize = calculateMaxBufferSize(indices);
//
//  bslma::Allocator *allocator = bslma::Default::defaultAllocator();
//  char *buffer = static_cast<char *>(allocator->allocate(bufferSize));
//
//  my_BufferAllocator bufferAllocator(buffer, bufferSize);
//
//  processIndices(&bufferAllocator, indices);
//..
// Next, we show the implementation of 'processIndices', within which we
// iterate over the market 'indices' that are passed to it:
//..
    static
    void processIndices(bdlma::ManagedAllocator *managedAllocator,
                        const IndexCollection&   indices)
        // Process the specified market 'indices' using the specified
        // 'managedAllocator' to supply memory.
    {
        for (IndexCollection::const_iterator citer = indices.begin();
                                             citer != indices.end(); ++citer) {

//..
// For each index, the 'SecurityCollection' comprising that index is created.
// All of the memory needs of the 'SecurityCollection' are provided by the
// 'managedAllocator'.  Note that even the memory for the footprint of the
// collection comes from the 'managedAllocator':
//..
            SecurityCollection *securities =
                new (managedAllocator->allocate(sizeof(SecurityCollection)))
                                          SecurityCollection(managedAllocator);

//..
// Next, we call 'loadIndex' to populate 'securities', followed by the call to
// 'processIndex'.  'loadIndex' also uses the 'managedAllocator', the details
// of which are not shown here:
//..
            loadIndex(securities, managedAllocator, *citer);

            processIndex(*securities, *citer);
//..
// After the index is processed, 'release' is called on the managed allocator
// making all of the buffer supplied to the allocator at construction available
// for reuse:
//..
            managedAllocator->release();
        }
//..
// Finally, we let the 'SecurityCollection' used to process the index go out of
// scope intentionally without deleting 'securities'.  The call to 'release'
// renders superfluous the need to call the 'SecurityCollection' destructor as
// well as the destructor of the contained 'my_SecurityAttributes' elements.
//..
    }
コード例 #2
0
std::unique_ptr<GeometricPrimitive> GeometricPrimitive::CreateGeoSphere( float diameter, size_t tessellation, bool rhcoords)
{
	// An undirected edge between two vertices, represented by a pair of indexes into a vertex array.
	// Becuse this edge is undirected, (a,b) is the same as (b,a).
	typedef std::pair<uint16_t, uint16_t> UndirectedEdge;

	// Makes an undirected edge. Rather than overloading comparison operators to give us the (a,b)==(b,a) property,
	// we'll just ensure that the larger of the two goes first. This'll simplify things greatly.
	auto makeUndirectedEdge = [](uint16_t a, uint16_t b)
	{
		return std::make_pair(max(a, b), min(a, b));
	};

	// Key: an edge
	// Value: the index of the vertex which lies midway between the two vertices pointed to by the key value
	// This map is used to avoid duplicating vertices when subdividing triangles along edges.
	typedef std::map<UndirectedEdge, uint16_t> EdgeSubdivisionMap;


	static const XMFLOAT3 OctahedronVertices[] =
	{
		// when looking down the negative z-axis (into the screen)
		XMFLOAT3(0, 1, 0), // 0 top
		XMFLOAT3(0, 0, -1), // 1 front
		XMFLOAT3(1, 0, 0), // 2 right
		XMFLOAT3(0, 0, 1), // 3 back
		XMFLOAT3(-1, 0, 0), // 4 left
		XMFLOAT3(0, -1, 0), // 5 bottom
	};
	static const uint16_t OctahedronIndices[] =
	{
		0, 1, 2, // top front-right face
		0, 2, 3, // top back-right face
		0, 3, 4, // top back-left face
		0, 4, 1, // top front-left face
		5, 1, 4, // bottom front-left face
		5, 4, 3, // bottom back-left face
		5, 3, 2, // bottom back-right face
		5, 2, 1, // bottom front-right face
	};

	const float radius = diameter / 2.0f;

	// Start with an octahedron; copy the data into the vertex/index collection.

	std::vector<XMFLOAT3> vertexPositions(std::begin(OctahedronVertices), std::end(OctahedronVertices));

	IndexCollection indices;
	indices.insert(indices.begin(), std::begin(OctahedronIndices), std::end(OctahedronIndices));

	// We know these values by looking at the above index list for the octahedron. Despite the subdivisions that are
	// about to go on, these values aren't ever going to change because the vertices don't move around in the array.
	// We'll need these values later on to fix the singularities that show up at the poles.
	const uint16_t northPoleIndex = 0;
	const uint16_t southPoleIndex = 5;

	for (size_t iSubdivision = 0; iSubdivision < tessellation; ++iSubdivision)
	{
		assert(indices.size() % 3 == 0); // sanity

		// We use this to keep track of which edges have already been subdivided.
		EdgeSubdivisionMap subdividedEdges;

		// The new index collection after subdivision.
		IndexCollection newIndices;

		const size_t triangleCount = indices.size() / 3;
		for (size_t iTriangle = 0; iTriangle < triangleCount; ++iTriangle)
		{
			// For each edge on this triangle, create a new vertex in the middle of that edge.
			// The winding order of the triangles we output are the same as the winding order of the inputs.

			// Indices of the vertices making up this triangle
			uint16_t iv0 = indices[iTriangle * 3 + 0];
			uint16_t iv1 = indices[iTriangle * 3 + 1];
			uint16_t iv2 = indices[iTriangle * 3 + 2];

			// Get the new vertices
			XMFLOAT3 v01; // vertex on the midpoint of v0 and v1
			XMFLOAT3 v12; // ditto v1 and v2
			XMFLOAT3 v20; // ditto v2 and v0
			uint16_t iv01; // index of v01
			uint16_t iv12; // index of v12
			uint16_t iv20; // index of v20

			// Function that, when given the index of two vertices, creates a new vertex at the midpoint of those vertices.
			auto divideEdge = [&](uint16_t i0, uint16_t i1, XMFLOAT3& outVertex, uint16_t& outIndex)
			{
				const UndirectedEdge edge = makeUndirectedEdge(i0, i1);

				// Check to see if we've already generated this vertex
				auto it = subdividedEdges.find(edge);
				if (it != subdividedEdges.end())
				{
					// We've already generated this vertex before
					outIndex = it->second; // the index of this vertex
					outVertex = vertexPositions[outIndex]; // and the vertex itself
				}
				else
				{
					// Haven't generated this vertex before: so add it now

					// outVertex = (vertices[i0] + vertices[i1]) / 2
					XMStoreFloat3(
						&outVertex,
						XMVectorScale(
						XMVectorAdd(XMLoadFloat3(&vertexPositions[i0]), XMLoadFloat3(&vertexPositions[i1])),
						0.5f
						)
						);

					outIndex = static_cast<uint16_t>(vertexPositions.size());
					CheckIndexOverflow(outIndex);
					vertexPositions.push_back(outVertex);

					// Now add it to the map.
					subdividedEdges.insert(std::make_pair(edge, outIndex));
				}
			};

			// Add/get new vertices and their indices
			divideEdge(iv0, iv1, v01, iv01);
			divideEdge(iv1, iv2, v12, iv12);
			divideEdge(iv0, iv2, v20, iv20);

			// Add the new indices. We have four new triangles from our original one:
			//        v0
			//        o
			//       /a\
						            //  v20 o---o v01
			//     /b\c/d\
						            // v2 o---o---o v1
			//       v12
			const uint16_t indicesToAdd[] =
			{
				iv0, iv01, iv20, // a
				iv20, iv12, iv2, // b
				iv20, iv01, iv12, // c
				iv01, iv1, iv12, // d
			};
			newIndices.insert(newIndices.end(), std::begin(indicesToAdd), std::end(indicesToAdd));
		}

		indices = std::move(newIndices);
	}

	// Now that we've completed subdivision, fill in the final vertex collection
	VertexCollection vertices;
	vertices.reserve(vertexPositions.size());
	for (auto it = vertexPositions.begin(); it != vertexPositions.end(); ++it)
	{
		auto vertexValue = *it;

		auto normal = XMVector3Normalize(XMLoadFloat3(&vertexValue));
		auto pos = XMVectorScale(normal, radius);

		XMFLOAT3 normalFloat3;
		XMStoreFloat3(&normalFloat3, normal);

		// calculate texture coordinates for this vertex
		float longitude = atan2(normalFloat3.x, -normalFloat3.z);
		float latitude = acos(normalFloat3.y);

		float u = longitude / XM_2PI + 0.5f;
		float v = latitude / XM_PI;

		auto texcoord = XMVectorSet(1.0f - u, v, 0.0f, 0.0f);
		vertices.push_back(VertexPositionNormalTexture(pos, normal, texcoord));
	}

	// There are a couple of fixes to do. One is a texture coordinate wraparound fixup. At some point, there will be
	// a set of triangles somewhere in the mesh with texture coordinates such that the wraparound across 0.0/1.0
	// occurs across that triangle. Eg. when the left hand side of the triangle has a U coordinate of 0.98 and the
	// right hand side has a U coordinate of 0.0. The intent is that such a triangle should render with a U of 0.98 to
	// 1.0, not 0.98 to 0.0. If we don't do this fixup, there will be a visible seam across one side of the sphere.
	// 
	// Luckily this is relatively easy to fix. There is a straight edge which runs down the prime meridian of the
	// completed sphere. If you imagine the vertices along that edge, they circumscribe a semicircular arc starting at
	// y=1 and ending at y=-1, and sweeping across the range of z=0 to z=1. x stays zero. It's along this edge that we
	// need to duplicate our vertices - and provide the correct texture coordinates.
	size_t preFixupVertexCount = vertices.size();
	for (size_t i = 0; i < preFixupVertexCount; ++i)
	{
		// This vertex is on the prime meridian if position.x and texcoord.u are both zero (allowing for small epsilon).
		bool isOnPrimeMeridian = XMVector2NearEqual(
			XMVectorSet(vertices[i].position.x, vertices[i].textureCoordinate.x, 0.0f, 0.0f),
			XMVectorZero(),
			XMVectorSplatEpsilon());

		if (isOnPrimeMeridian)
		{
			size_t newIndex = vertices.size(); // the index of this vertex that we're about to add
			CheckIndexOverflow(newIndex);

			// copy this vertex, correct the texture coordinate, and add the vertex
			VertexPositionNormalTexture v = vertices[i];
			v.textureCoordinate.x = 1.0f;
			vertices.push_back(v);

			// Now find all the triangles which contain this vertex and update them if necessary
			for (size_t j = 0; j < indices.size(); j += 3)
			{
				uint16_t* triIndex0 = &indices[j + 0];
				uint16_t* triIndex1 = &indices[j + 1];
				uint16_t* triIndex2 = &indices[j + 2];

				if (*triIndex0 == i)
				{
					// nothing; just keep going
				}
				else if (*triIndex1 == i)
				{
					std::swap(triIndex0, triIndex1); // swap the pointers (not the values)
				}
				else if (*triIndex2 == i)
				{
					std::swap(triIndex0, triIndex2); // swap the pointers (not the values)
				}
				else
				{
					// this triangle doesn't use the vertex we're interested in
					continue;
				}

				// If we got to this point then triIndex0 is the pointer to the index to the vertex we're looking at
				assert(*triIndex0 == i);
				assert(*triIndex1 != i && *triIndex2 != i); // assume no degenerate triangles

				const VertexPositionNormalTexture& v0 = vertices[*triIndex0];
				const VertexPositionNormalTexture& v1 = vertices[*triIndex1];
				const VertexPositionNormalTexture& v2 = vertices[*triIndex2];

				// check the other two vertices to see if we might need to fix this triangle

				if (abs(v0.textureCoordinate.x - v1.textureCoordinate.x) > 0.5f ||
					abs(v0.textureCoordinate.x - v2.textureCoordinate.x) > 0.5f)
				{
					// yep; replace the specified index to point to the new, corrected vertex
					*triIndex0 = static_cast<uint16_t>(newIndex);
				}
			}
		}
	}

	// And one last fix we need to do: the poles. A common use-case of a sphere mesh is to map a rectangular texture onto
	// it. If that happens, then the poles become singularities which map the entire top and bottom rows of the texture
	// onto a single point. In general there's no real way to do that right. But to match the behavior of non-geodesic
	// spheres, we need to duplicate the pole vertex for every triangle that uses it. This will introduce seams near the
	// poles, but reduce stretching.
	auto fixPole = [&](size_t poleIndex)
	{
		auto poleVertex = vertices[poleIndex];
		bool overwrittenPoleVertex = false; // overwriting the original pole vertex saves us one vertex

		for (size_t i = 0; i < indices.size(); i += 3)
		{
			// These pointers point to the three indices which make up this triangle. pPoleIndex is the pointer to the
			// entry in the index array which represents the pole index, and the other two pointers point to the other
			// two indices making up this triangle.
			uint16_t* pPoleIndex;
			uint16_t* pOtherIndex0;
			uint16_t* pOtherIndex1;
			if (indices[i + 0] == poleIndex)
			{
				pPoleIndex = &indices[i + 0];
				pOtherIndex0 = &indices[i + 1];
				pOtherIndex1 = &indices[i + 2];
			}
			else if (indices[i + 1] == poleIndex)
			{
				pPoleIndex = &indices[i + 1];
				pOtherIndex0 = &indices[i + 2];
				pOtherIndex1 = &indices[i + 0];
			}
			else if (indices[i + 2] == poleIndex)
			{
				pPoleIndex = &indices[i + 2];
				pOtherIndex0 = &indices[i + 0];
				pOtherIndex1 = &indices[i + 1];
			}
			else
			{
				continue;
			}

			const auto& otherVertex0 = vertices[*pOtherIndex0];
			const auto& otherVertex1 = vertices[*pOtherIndex1];

			// Calculate the texcoords for the new pole vertex, add it to the vertices and update the index
			VertexPositionNormalTexture newPoleVertex = poleVertex;
			newPoleVertex.textureCoordinate.x = (otherVertex0.textureCoordinate.x + otherVertex1.textureCoordinate.x) / 2;
			newPoleVertex.textureCoordinate.y = poleVertex.textureCoordinate.y;

			if (!overwrittenPoleVertex)
			{
				vertices[poleIndex] = newPoleVertex;
				overwrittenPoleVertex = true;
			}
			else
			{
				CheckIndexOverflow(vertices.size());

				*pPoleIndex = static_cast<uint16_t>(vertices.size());
				vertices.push_back(newPoleVertex);
			}
		}
	};

	fixPole(northPoleIndex);
	fixPole(southPoleIndex);

	// Create the primitive object.
	std::unique_ptr<GeometricPrimitive> primitive(new GeometricPrimitive());

	primitive->pImpl->Initialize( vertices, indices, rhcoords);
	return primitive;
}