IndexCollection operator&&(IndexCollection const& coll1, IndexCollection const& coll2) { std::vector<plint> result; std::vector<plint> const& ind1 = coll1.get(); std::vector<plint> const& ind2 = coll2.get(); std::vector<plint>::const_iterator iter_ind1 = ind1.begin(); std::vector<plint>::const_iterator iter_ind2 = ind2.begin(); while(!(iter_ind1==ind1.end() || iter_ind2==ind2.end())) { if (*iter_ind1 == *iter_ind2) { result.push_back(*iter_ind1); ++iter_ind1; ++iter_ind2; } else { if (*iter_ind1 < *iter_ind2) { ++iter_ind1; } else { ++iter_ind2; } } } return IndexCollection(result); }
std::unique_ptr<GeometricPrimitive> GeometricPrimitive::CreateTetrahedron( float size /*= 1*/, bool rhcoords /*= true*/) { VertexCollection vertices; IndexCollection indices; static const XMVECTORF32 verts[4] = { { 0.f, 0.f, 1.f }, { 2.f*SQRT2 / 3.f, 0.f, -1.f / 3.f }, { -SQRT2 / 3.f, SQRT6 / 3.f, -1.f / 3.f }, { -SQRT2 / 3.f, -SQRT6 / 3.f, -1.f / 3.f } }; static const uint32_t faces[4 * 3] = { 0, 1, 2, 0, 2, 3, 0, 3, 1, 1, 3, 2, }; for (size_t j = 0; j < _countof(faces); j += 3) { uint32_t v0 = faces[j]; uint32_t v1 = faces[j + 1]; uint32_t v2 = faces[j + 2]; XMVECTOR normal = XMVector3Cross(verts[v1].v - verts[v0].v, verts[v2].v - verts[v0].v); normal = XMVector3Normalize(normal); size_t base = vertices.size(); indices.push_back(base); indices.push_back(base + 1); indices.push_back(base + 2); // Duplicate vertices to use face normals XMVECTOR position = XMVectorScale(verts[v0], size); vertices.push_back(VertexPositionNormalTexture(position, normal, g_XMZero /* 0, 0 */)); position = XMVectorScale(verts[v1], size); vertices.push_back(VertexPositionNormalTexture(position, normal, g_XMIdentityR0 /* 1, 0 */)); position = XMVectorScale(verts[v2], size); vertices.push_back(VertexPositionNormalTexture(position, normal, g_XMIdentityR1 /* 0, 1 */)); } assert(vertices.size() == 4 * 3); assert(indices.size() == 4 * 3); // Create the primitive object. std::unique_ptr<GeometricPrimitive> primitive(new GeometricPrimitive()); primitive->pImpl->Initialize( vertices, indices, !rhcoords); return primitive; }
//-------------------------------------------------------------------------------------- // Tetrahedron //-------------------------------------------------------------------------------------- void DirectX::ComputeTetrahedron(VertexCollection& vertices, IndexCollection& indices, float size, bool rhcoords) { vertices.clear(); indices.clear(); static const XMVECTORF32 verts[4] = { { { { 0.f, 0.f, 1.f, 0 } } }, { { { 2.f*SQRT2 / 3.f, 0.f, -1.f / 3.f, 0 } } }, { { { -SQRT2 / 3.f, SQRT6 / 3.f, -1.f / 3.f, 0 } } }, { { { -SQRT2 / 3.f, -SQRT6 / 3.f, -1.f / 3.f, 0 } } } }; static const uint32_t faces[4 * 3] = { 0, 1, 2, 0, 2, 3, 0, 3, 1, 1, 3, 2, }; for (size_t j = 0; j < _countof(faces); j += 3) { uint32_t v0 = faces[j]; uint32_t v1 = faces[j + 1]; uint32_t v2 = faces[j + 2]; XMVECTOR normal = XMVector3Cross( XMVectorSubtract(verts[v1].v, verts[v0].v), XMVectorSubtract(verts[v2].v, verts[v0].v)); normal = XMVector3Normalize(normal); size_t base = vertices.size(); index_push_back(indices, base); index_push_back(indices, base + 1); index_push_back(indices, base + 2); // Duplicate vertices to use face normals XMVECTOR position = XMVectorScale(verts[v0], size); vertices.push_back(VertexPositionNormalTexture(position, normal, g_XMZero /* 0, 0 */)); position = XMVectorScale(verts[v1], size); vertices.push_back(VertexPositionNormalTexture(position, normal, g_XMIdentityR0 /* 1, 0 */)); position = XMVectorScale(verts[v2], size); vertices.push_back(VertexPositionNormalTexture(position, normal, g_XMIdentityR1 /* 0, 1 */)); } // Built LH above if (rhcoords) ReverseWinding(indices, vertices); assert(vertices.size() == 4 * 3); assert(indices.size() == 4 * 3); }
std::unique_ptr<GeometricPrimitive> GeometricPrimitive::CreateCone( float diameter /*= 1*/, float height /*= 1*/, size_t tessellation /*= 32*/, bool rhcoords /*= true*/) { VertexCollection vertices; IndexCollection indices; if (tessellation < 3) throw std::out_of_range("tesselation parameter out of range"); height /= 2; XMVECTOR topOffset = g_XMIdentityR1 * height; float radius = diameter / 2; size_t stride = tessellation + 1; // Create a ring of triangles around the outside of the cone. for (size_t i = 0; i <= tessellation; i++) { XMVECTOR circlevec = GetCircleVector(i, tessellation); XMVECTOR sideOffset = circlevec * radius; float u = (float)i / tessellation; XMVECTOR textureCoordinate = XMLoadFloat(&u); XMVECTOR pt = sideOffset - topOffset; XMVECTOR normal = XMVector3Cross(GetCircleTangent(i, tessellation), topOffset - pt); normal = XMVector3Normalize(normal); // Duplicate the top vertex for distinct normals vertices.push_back(VertexPositionNormalTexture(topOffset, normal, g_XMZero)); vertices.push_back(VertexPositionNormalTexture(pt, normal, textureCoordinate + g_XMIdentityR1)); indices.push_back(i * 2); indices.push_back((i * 2 + 3) % (stride * 2)); indices.push_back((i * 2 + 1) % (stride * 2)); } // Create flat triangle fan caps to seal the bottom. CreateCylinderCap(vertices, indices, tessellation, height, radius, false); // Create the primitive object. std::unique_ptr<GeometricPrimitive> primitive(new GeometricPrimitive()); primitive->pImpl->Initialize( vertices, indices, rhcoords); return primitive; }
void Symtab::AppendSymbolNamesToMap(const IndexCollection &indexes, bool add_demangled, bool add_mangled, NameToIndexMap &name_to_index_map) const { if (add_demangled || add_mangled) { static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); Timer scoped_timer(func_cat, "%s", LLVM_PRETTY_FUNCTION); std::lock_guard<std::recursive_mutex> guard(m_mutex); // Create the name index vector to be able to quickly search by name NameToIndexMap::Entry entry; const size_t num_indexes = indexes.size(); for (size_t i = 0; i < num_indexes; ++i) { entry.value = indexes[i]; assert(i < m_symbols.size()); const Symbol *symbol = &m_symbols[entry.value]; const Mangled &mangled = symbol->GetMangled(); if (add_demangled) { entry.cstring = mangled.GetDemangledName(symbol->GetLanguage()); if (entry.cstring) name_to_index_map.Append(entry); } if (add_mangled) { entry.cstring = mangled.GetMangledName(); if (entry.cstring) name_to_index_map.Append(entry); } } } }
// Initializes a geometric primitive instance that will draw the specified vertex and index data. void GeometricPrimitive::Impl::Initialize( const VertexCollection& vertices, const IndexCollection& indices, _In_opt_ ID3D12Device* device) { if (vertices.size() >= USHRT_MAX) throw std::exception("Too many vertices for 16-bit index buffer"); if (indices.size() > UINT32_MAX) throw std::exception("Too many indices"); // Vertex data uint64_t sizeInBytes = uint64_t(vertices.size()) * sizeof(vertices[0]); if (sizeInBytes > uint64_t(D3D12_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM * 1024u * 1024u)) throw std::exception("VB too large for DirectX 12"); auto vertSizeBytes = static_cast<size_t>(sizeInBytes); mVertexBuffer = GraphicsMemory::Get(device).Allocate(vertSizeBytes); auto verts = reinterpret_cast<const uint8_t*>(vertices.data()); memcpy(mVertexBuffer.Memory(), verts, vertSizeBytes); // Index data sizeInBytes = uint64_t(indices.size()) * sizeof(indices[0]); if (sizeInBytes > uint64_t(D3D12_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM * 1024u * 1024u)) throw std::exception("IB too large for DirectX 12"); auto indSizeBytes = static_cast<size_t>(sizeInBytes); mIndexBuffer = GraphicsMemory::Get(device).Allocate(indSizeBytes); auto ind = reinterpret_cast<const uint8_t*>(indices.data()); memcpy(mIndexBuffer.Memory(), ind, indSizeBytes); // Record index count for draw mIndexCount = static_cast<UINT>(indices.size()); // Create views mVertexBufferView.BufferLocation = mVertexBuffer.GpuAddress(); mVertexBufferView.StrideInBytes = static_cast<UINT>(sizeof(VertexCollection::value_type)); mVertexBufferView.SizeInBytes = static_cast<UINT>(mVertexBuffer.Size()); mIndexBufferView.BufferLocation = mIndexBuffer.GpuAddress(); mIndexBufferView.SizeInBytes = static_cast<UINT>(mIndexBuffer.Size()); mIndexBufferView.Format = DXGI_FORMAT_R16_UINT; }
//-------------------------------------------------------------------------------------- // Torus //-------------------------------------------------------------------------------------- void DirectX::ComputeTorus(VertexCollection& vertices, IndexCollection& indices, float diameter, float thickness, size_t tessellation, bool rhcoords) { vertices.clear(); indices.clear(); if (tessellation < 3) throw std::out_of_range("tesselation parameter out of range"); size_t stride = tessellation + 1; // First we loop around the main ring of the torus. for (size_t i = 0; i <= tessellation; i++) { float u = float(i) / tessellation; float outerAngle = i * XM_2PI / tessellation - XM_PIDIV2; // Create a transform matrix that will align geometry to // slice perpendicularly though the current ring position. XMMATRIX transform = XMMatrixTranslation(diameter / 2, 0, 0) * XMMatrixRotationY(outerAngle); // Now we loop along the other axis, around the side of the tube. for (size_t j = 0; j <= tessellation; j++) { float v = 1 - float(j) / tessellation; float innerAngle = j * XM_2PI / tessellation + XM_PI; float dx, dy; XMScalarSinCos(&dy, &dx, innerAngle); // Create a vertex. XMVECTOR normal = XMVectorSet(dx, dy, 0, 0); XMVECTOR position = XMVectorScale(normal, thickness / 2); XMVECTOR textureCoordinate = XMVectorSet(u, v, 0, 0); position = XMVector3Transform(position, transform); normal = XMVector3TransformNormal(normal, transform); vertices.push_back(VertexPositionNormalTexture(position, normal, textureCoordinate)); // And create indices for two triangles. size_t nextI = (i + 1) % stride; size_t nextJ = (j + 1) % stride; index_push_back(indices, i * stride + j); index_push_back(indices, i * stride + nextJ); index_push_back(indices, nextI * stride + j); index_push_back(indices, i * stride + nextJ); index_push_back(indices, nextI * stride + nextJ); index_push_back(indices, nextI * stride + j); } } // Build RH above if (!rhcoords) ReverseWinding(indices, vertices); }
// Helper creates a triangle fan to close the end of a cylinder / cone static void CreateCylinderCap(VertexCollection& vertices, IndexCollection& indices, size_t tessellation, float height, float radius, bool isTop) { // Create cap indices. for (size_t i = 0; i < tessellation - 2; i++) { size_t i1 = (i + 1) % tessellation; size_t i2 = (i + 2) % tessellation; if (isTop) { std::swap(i1, i2); } size_t vbase = vertices.size(); indices.push_back(vbase); indices.push_back(vbase + i1); indices.push_back(vbase + i2); } // Which end of the cylinder is this? XMVECTOR normal = g_XMIdentityR1; XMVECTOR textureScale = g_XMNegativeOneHalf; if (!isTop) { normal = -normal; textureScale *= g_XMNegateX; } // Create cap vertices. for (size_t i = 0; i < tessellation; i++) { XMVECTOR circleVector = GetCircleVector(i, tessellation); XMVECTOR position = (circleVector * radius) + (normal * height); XMVECTOR textureCoordinate = XMVectorMultiplyAdd(XMVectorSwizzle<0, 2, 3, 3>(circleVector), textureScale, g_XMOneHalf); vertices.push_back(VertexPositionNormalTexture(position, normal, textureCoordinate)); } }
_Use_decl_annotations_ void GeometricPrimitive::Impl::Initialize(VertexCollection& vertices, IndexCollection& indices, bool rhcoords) { if (vertices.size() >= USHRT_MAX) throw std::exception("Too many vertices for 16-bit index buffer"); ID3D11Device* device = g_objDeviecManager.GetDevice(); CreateBuffer(device, vertices, D3D11_BIND_VERTEX_BUFFER, &mVertexBuffer); CreateBuffer(device, indices, D3D11_BIND_INDEX_BUFFER, &mIndexBuffer); mIndexCount = static_cast<UINT>(indices.size()); }
// 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. //.. }
// Initializes a geometric primitive instance that will draw the specified vertex and index data. void GeometricPrimitive::Impl::Initialize(_In_ ID3D11DeviceContext* deviceContext, VertexCollection const& vertices, IndexCollection const& indices) { mResources = sharedResourcesPool.DemandCreate(deviceContext); ComPtr<ID3D11Device> device; deviceContext->GetDevice(&device); CreateBuffer(device.Get(), vertices, D3D11_BIND_VERTEX_BUFFER, &mVertexBuffer); CreateBuffer(device.Get(), indices, D3D11_BIND_INDEX_BUFFER, &mIndexBuffer); mIndexCount = (UINT)indices.size(); }
// Creates a cone primitive. void DirectX::ComputeCone(VertexCollection& vertices, IndexCollection& indices, float diameter, float height, size_t tessellation, bool rhcoords) { vertices.clear(); indices.clear(); if (tessellation < 3) throw std::out_of_range("tesselation parameter out of range"); height /= 2; XMVECTOR topOffset = XMVectorScale(g_XMIdentityR1, height); float radius = diameter / 2; size_t stride = tessellation + 1; // Create a ring of triangles around the outside of the cone. for (size_t i = 0; i <= tessellation; i++) { XMVECTOR circlevec = GetCircleVector(i, tessellation); XMVECTOR sideOffset = XMVectorScale(circlevec, radius); float u = float(i) / tessellation; XMVECTOR textureCoordinate = XMLoadFloat(&u); XMVECTOR pt = XMVectorSubtract(sideOffset, topOffset); XMVECTOR normal = XMVector3Cross( GetCircleTangent(i, tessellation), XMVectorSubtract(topOffset, pt)); normal = XMVector3Normalize(normal); // Duplicate the top vertex for distinct normals vertices.push_back(VertexPositionNormalTexture(topOffset, normal, g_XMZero)); vertices.push_back(VertexPositionNormalTexture(pt, normal, XMVectorAdd(textureCoordinate, g_XMIdentityR1))); index_push_back(indices, i * 2); index_push_back(indices, (i * 2 + 3) % (stride * 2)); index_push_back(indices, (i * 2 + 1) % (stride * 2)); } // Create flat triangle fan caps to seal the bottom. CreateCylinderCap(vertices, indices, tessellation, height, radius, false); // Build RH above if (!rhcoords) ReverseWinding(indices, vertices); }
IndexCollection operator!(IndexCollection const& coll) { std::vector<plint> result; std::vector<plint> const& ind = coll.get(); std::vector<plint>::const_iterator iter_ind = ind.begin(); plint negative_ind = 0; for (; iter_ind != ind.end(); ++iter_ind) { while (negative_ind < *iter_ind) { result.push_back(negative_ind++); } ++negative_ind; } return IndexCollection(result); }
// Initializes a geometric primitive instance that will draw the specified vertex and index data. _Use_decl_annotations_ void GeometricPrimitive::Impl::Initialize(ID3D11DeviceContext* deviceContext, const VertexCollection& vertices, const IndexCollection& indices) { if ( vertices.size() >= USHRT_MAX ) throw std::exception("Too many vertices for 16-bit index buffer"); mResources = sharedResourcesPool.DemandCreate(deviceContext); ComPtr<ID3D11Device> device; deviceContext->GetDevice(&device); CreateBuffer(device.Get(), vertices, D3D11_BIND_VERTEX_BUFFER, &mVertexBuffer); CreateBuffer(device.Get(), indices, D3D11_BIND_INDEX_BUFFER, &mIndexBuffer); mIndexCount = static_cast<UINT>( indices.size() ); }
// Creates a cylinder primitive. std::unique_ptr<GeometricPrimitive> GeometricPrimitive::CreateCylinder(_In_ ID3D11DeviceContext* deviceContext, float height, float diameter, size_t tessellation) { VertexCollection vertices; IndexCollection indices; if (tessellation < 3) throw std::out_of_range("tesselation parameter out of range"); height /= 2; XMVECTOR topOffset = g_XMIdentityR1 * height; float radius = diameter / 2; size_t stride = tessellation + 1; // Create a ring of triangles around the outside of the cylinder. for (size_t i = 0; i <= tessellation; i++) { XMVECTOR normal = GetCircleVector(i, tessellation); XMVECTOR sideOffset = normal * radius; float u = (float)i / tessellation; XMVECTOR textureCoordinate = XMLoadFloat(&u); vertices.push_back(VertexPositionNormalTexture(sideOffset + topOffset, normal, textureCoordinate)); vertices.push_back(VertexPositionNormalTexture(sideOffset - topOffset, normal, textureCoordinate + g_XMIdentityR1)); indices.push_back(i * 2); indices.push_back((i * 2 + 2) % (stride * 2)); indices.push_back(i * 2 + 1); indices.push_back(i * 2 + 1); indices.push_back((i * 2 + 2) % (stride * 2)); indices.push_back((i * 2 + 3) % (stride * 2)); } // Create flat triangle fan caps to seal the top and bottom. CreateCylinderCap(vertices, indices, tessellation, height, radius, true); CreateCylinderCap(vertices, indices, tessellation, height, radius, false); // Create the primitive object. std::unique_ptr<GeometricPrimitive> primitive(new GeometricPrimitive()); primitive->pImpl->Initialize(deviceContext, vertices, indices); return primitive; }
// Tessellates the specified bezier patch. static void TessellatePatch(VertexCollection& vertices, IndexCollection& indices, TeapotPatch const& patch, size_t tessellation, FXMVECTOR scale, bool isMirrored) { // Look up the 16 control points for this patch. XMVECTOR controlPoints[16]; for (int i = 0; i < 16; i++) { controlPoints[i] = TeapotControlPoints[patch.indices[i]] * scale; } // Create the index data. Bezier::CreatePatchIndices(tessellation, isMirrored, [&](size_t index) { indices.push_back(vertices.size() + index); }); // Create the vertex data. Bezier::CreatePatchVertices(controlPoints, tessellation, isMirrored, [&](FXMVECTOR position, FXMVECTOR normal, FXMVECTOR textureCoordinate) { vertices.push_back(VertexPositionNormalTexture(position, normal, textureCoordinate)); }); }
// Creates a teapot primitive. void DirectX::ComputeTeapot(VertexCollection& vertices, IndexCollection& indices, float size, size_t tessellation, bool rhcoords) { vertices.clear(); indices.clear(); if (tessellation < 1) throw std::out_of_range("tesselation parameter out of range"); XMVECTOR scaleVector = XMVectorReplicate(size); XMVECTOR scaleNegateX = XMVectorMultiply(scaleVector, g_XMNegateX); XMVECTOR scaleNegateZ = XMVectorMultiply(scaleVector, g_XMNegateZ); XMVECTOR scaleNegateXZ = XMVectorMultiply(scaleVector, XMVectorMultiply(g_XMNegateX, g_XMNegateZ)); for (size_t i = 0; i < _countof(TeapotPatches); i++) { TeapotPatch const& patch = TeapotPatches[i]; // Because the teapot is symmetrical from left to right, we only store // data for one side, then tessellate each patch twice, mirroring in X. TessellatePatch(vertices, indices, patch, tessellation, scaleVector, false); TessellatePatch(vertices, indices, patch, tessellation, scaleNegateX, true); if (patch.mirrorZ) { // Some parts of the teapot (the body, lid, and rim, but not the // handle or spout) are also symmetrical from front to back, so // we tessellate them four times, mirroring in Z as well as X. TessellatePatch(vertices, indices, patch, tessellation, scaleNegateZ, true); TessellatePatch(vertices, indices, patch, tessellation, scaleNegateXZ, false); } } // Built RH above if (!rhcoords) ReverseWinding(indices, vertices); }
void Symtab::AppendSymbolNamesToMap (const IndexCollection &indexes, bool add_demangled, bool add_mangled, NameToIndexMap &name_to_index_map) const { if (add_demangled || add_mangled) { Timer scoped_timer (__PRETTY_FUNCTION__, "%s", __PRETTY_FUNCTION__); Mutex::Locker locker (m_mutex); // Create the name index vector to be able to quickly search by name NameToIndexMap::Entry entry; const size_t num_indexes = indexes.size(); for (size_t i=0; i<num_indexes; ++i) { entry.value = indexes[i]; assert (i < m_symbols.size()); const Symbol *symbol = &m_symbols[entry.value]; const Mangled &mangled = symbol->GetMangled(); if (add_demangled) { entry.cstring = mangled.GetDemangledName().GetCString(); if (entry.cstring && entry.cstring[0]) name_to_index_map.Append (entry); } if (add_mangled) { entry.cstring = mangled.GetMangledName().GetCString(); if (entry.cstring && entry.cstring[0]) name_to_index_map.Append (entry); } } } }
std::unique_ptr<GeometricPrimitive> GeometricPrimitive::CreateDodecahedron( float size /*= 1*/, bool rhcoords /*= true*/) { VertexCollection vertices; IndexCollection indices; static const float a = 1.f / SQRT3; static const float b = 0.356822089773089931942f; // sqrt( ( 3 - sqrt(5) ) / 6 ) static const float c = 0.934172358962715696451f; // sqrt( ( 3 + sqrt(5) ) / 6 ); static const XMVECTORF32 verts[20] = { { a, a, a }, { a, a, -a }, { a, -a, a }, { a, -a, -a }, { -a, a, a }, { -a, a, -a }, { -a, -a, a }, { -a, -a, -a }, { b, c, 0 }, { -b, c, 0 }, { b, -c, 0 }, { -b, -c, 0 }, { c, 0, b }, { c, 0, -b }, { -c, 0, b }, { -c, 0, -b }, { 0, b, c }, { 0, -b, c }, { 0, b, -c }, { 0, -b, -c } }; static const uint32_t faces[12 * 5] = { 0, 8, 9, 4, 16, 0, 16, 17, 2, 12, 12, 2, 10, 3, 13, 9, 5, 15, 14, 4, 3, 19, 18, 1, 13, 7, 11, 6, 14, 15, 0, 12, 13, 1, 8, 8, 1, 18, 5, 9, 16, 4, 14, 6, 17, 6, 11, 10, 2, 17, 7, 15, 5, 18, 19, 7, 19, 3, 10, 11, }; static const XMVECTORF32 textureCoordinates[5] = { { 0.654508f, 0.0244717f }, { 0.0954915f, 0.206107f }, { 0.0954915f, 0.793893f }, { 0.654508f, 0.975528f }, { 1.f, 0.5f } }; static const uint32_t textureIndex[12][5] = { { 0, 1, 2, 3, 4 }, { 2, 3, 4, 0, 1 }, { 4, 0, 1, 2, 3 }, { 1, 2, 3, 4, 0 }, { 2, 3, 4, 0, 1 }, { 0, 1, 2, 3, 4 }, { 1, 2, 3, 4, 0 }, { 4, 0, 1, 2, 3 }, { 4, 0, 1, 2, 3 }, { 1, 2, 3, 4, 0 }, { 0, 1, 2, 3, 4 }, { 2, 3, 4, 0, 1 }, }; size_t t = 0; for (size_t j = 0; j < _countof(faces); j += 5, ++t) { uint32_t v0 = faces[j]; uint32_t v1 = faces[j + 1]; uint32_t v2 = faces[j + 2]; uint32_t v3 = faces[j + 3]; uint32_t v4 = faces[j + 4]; XMVECTOR normal = XMVector3Cross(verts[v1].v - verts[v0].v, verts[v2].v - verts[v0].v); normal = XMVector3Normalize(normal); size_t base = vertices.size(); indices.push_back(base); indices.push_back(base + 1); indices.push_back(base + 2); indices.push_back(base); indices.push_back(base + 2); indices.push_back(base + 3); indices.push_back(base); indices.push_back(base + 3); indices.push_back(base + 4); // Duplicate vertices to use face normals XMVECTOR position = XMVectorScale(verts[v0], size); vertices.push_back(VertexPositionNormalTexture(position, normal, textureCoordinates[textureIndex[t][0]])); position = XMVectorScale(verts[v1], size); vertices.push_back(VertexPositionNormalTexture(position, normal, textureCoordinates[textureIndex[t][1]])); position = XMVectorScale(verts[v2], size); vertices.push_back(VertexPositionNormalTexture(position, normal, textureCoordinates[textureIndex[t][2]])); position = XMVectorScale(verts[v3], size); vertices.push_back(VertexPositionNormalTexture(position, normal, textureCoordinates[textureIndex[t][3]])); position = XMVectorScale(verts[v4], size); vertices.push_back(VertexPositionNormalTexture(position, normal, textureCoordinates[textureIndex[t][4]])); } assert(vertices.size() == 12 * 5); assert(indices.size() == 12 * 3 * 3); // Create the primitive object. std::unique_ptr<GeometricPrimitive> primitive(new GeometricPrimitive()); primitive->pImpl->Initialize( vertices, indices, !rhcoords); return primitive; }
//-------------------------------------------------------------------------------------- // Icosahedron //-------------------------------------------------------------------------------------- void DirectX::ComputeIcosahedron(VertexCollection& vertices, IndexCollection& indices, float size, bool rhcoords) { vertices.clear(); indices.clear(); static const float t = 1.618033988749894848205f; // (1 + sqrt(5)) / 2 static const float t2 = 1.519544995837552493271f; // sqrt( 1 + sqr( (1 + sqrt(5)) / 2 ) ) static const XMVECTORF32 verts[12] = { { { { t / t2, 1.f / t2, 0, 0 } } }, { { { -t / t2, 1.f / t2, 0, 0 } } }, { { { t / t2, -1.f / t2, 0, 0 } } }, { { { -t / t2, -1.f / t2, 0, 0 } } }, { { { 1.f / t2, 0, t / t2, 0 } } }, { { { 1.f / t2, 0, -t / t2, 0 } } }, { { { -1.f / t2, 0, t / t2, 0 } } }, { { { -1.f / t2, 0, -t / t2, 0 } } }, { { { 0, t / t2, 1.f / t2, 0 } } }, { { { 0, -t / t2, 1.f / t2, 0 } } }, { { { 0, t / t2, -1.f / t2, 0 } } }, { { { 0, -t / t2, -1.f / t2, 0 } } } }; static const uint32_t faces[20 * 3] = { 0, 8, 4, 0, 5, 10, 2, 4, 9, 2, 11, 5, 1, 6, 8, 1, 10, 7, 3, 9, 6, 3, 7, 11, 0, 10, 8, 1, 8, 10, 2, 9, 11, 3, 11, 9, 4, 2, 0, 5, 0, 2, 6, 1, 3, 7, 3, 1, 8, 6, 4, 9, 4, 6, 10, 5, 7, 11, 7, 5 }; for (size_t j = 0; j < _countof(faces); j += 3) { uint32_t v0 = faces[j]; uint32_t v1 = faces[j + 1]; uint32_t v2 = faces[j + 2]; XMVECTOR normal = XMVector3Cross( XMVectorSubtract(verts[v1].v, verts[v0].v), XMVectorSubtract(verts[v2].v, verts[v0].v)); normal = XMVector3Normalize(normal); size_t base = vertices.size(); index_push_back(indices, base); index_push_back(indices, base + 1); index_push_back(indices, base + 2); // Duplicate vertices to use face normals XMVECTOR position = XMVectorScale(verts[v0], size); vertices.push_back(VertexPositionNormalTexture(position, normal, g_XMZero /* 0, 0 */)); position = XMVectorScale(verts[v1], size); vertices.push_back(VertexPositionNormalTexture(position, normal, g_XMIdentityR0 /* 1, 0 */)); position = XMVectorScale(verts[v2], size); vertices.push_back(VertexPositionNormalTexture(position, normal, g_XMIdentityR1 /* 0, 1 */)); } // Built LH above if (rhcoords) ReverseWinding(indices, vertices); assert(vertices.size() == 20 * 3); assert(indices.size() == 20 * 3); }
std::unique_ptr<GeometricPrimitive> GeometricPrimitive::CreateTorus( float diameter /*= 1*/, float thickness /*= 0.333f*/, size_t tessellation /*= 32*/, bool rhcoords /*= true*/) { VertexCollection vertices; IndexCollection indices; if (tessellation < 3) throw std::out_of_range("tesselation parameter out of range"); size_t stride = tessellation + 1; // First we loop around the main ring of the torus. for (size_t i = 0; i <= tessellation; i++) { float u = (float)i / tessellation; float outerAngle = i * XM_2PI / tessellation - XM_PIDIV2; // Create a transform matrix that will align geometry to // slice perpendicularly though the current ring position. XMMATRIX transform = XMMatrixTranslation(diameter / 2, 0, 0) * XMMatrixRotationY(outerAngle); // Now we loop along the other axis, around the side of the tube. for (size_t j = 0; j <= tessellation; j++) { float v = 1 - (float)j / tessellation; float innerAngle = j * XM_2PI / tessellation + XM_PI; float dx, dy; XMScalarSinCos(&dy, &dx, innerAngle); // Create a vertex. XMVECTOR normal = XMVectorSet(dx, dy, 0, 0); XMVECTOR position = normal * thickness / 2; XMVECTOR textureCoordinate = XMVectorSet(u, v, 0, 0); position = XMVector3Transform(position, transform); normal = XMVector3TransformNormal(normal, transform); vertices.push_back(VertexPositionNormalTexture(position, normal, textureCoordinate)); // And create indices for two triangles. size_t nextI = (i + 1) % stride; size_t nextJ = (j + 1) % stride; indices.push_back(i * stride + j); indices.push_back(i * stride + nextJ); indices.push_back(nextI * stride + j); indices.push_back(i * stride + nextJ); indices.push_back(nextI * stride + nextJ); indices.push_back(nextI * stride + j); } } // Create the primitive object. std::unique_ptr<GeometricPrimitive> primitive(new GeometricPrimitive()); primitive->pImpl->Initialize( vertices, indices, rhcoords); return primitive; }
std::unique_ptr<GeometricPrimitive> GeometricPrimitive::CreateSphere( float diameter /*= 1*/, size_t tessellation /*= 16*/, bool rhcoords /*= true*/) { VertexCollection vertices; IndexCollection indices; if (tessellation < 3) throw std::out_of_range("tesselation parameter out of range"); size_t verticalSegments = tessellation; size_t horizontalSegments = tessellation * 2; float radius = diameter / 2; // Create rings of vertices at progressively higher latitudes. for (size_t i = 0; i <= verticalSegments; i++) { float v = 1 - (float)i / verticalSegments; float latitude = (i * XM_PI / verticalSegments) - XM_PIDIV2; float dy, dxz; XMScalarSinCos(&dy, &dxz, latitude); // Create a single ring of vertices at this latitude. for (size_t j = 0; j <= horizontalSegments; j++) { float u = (float)j / horizontalSegments; float longitude = j * XM_2PI / horizontalSegments; float dx, dz; XMScalarSinCos(&dx, &dz, longitude); dx *= dxz; dz *= dxz; XMVECTOR normal = XMVectorSet(dx, dy, dz, 0); XMVECTOR textureCoordinate = XMVectorSet(u, v, 0, 0); vertices.push_back(VertexPositionNormalTexture(normal * radius, normal, textureCoordinate)); } } // Fill the index buffer with triangles joining each pair of latitude rings. size_t stride = horizontalSegments + 1; for (size_t i = 0; i < verticalSegments; i++) { for (size_t j = 0; j <= horizontalSegments; j++) { size_t nextI = i + 1; size_t nextJ = (j + 1) % stride; indices.push_back(i * stride + j); indices.push_back(nextI * stride + j); indices.push_back(i * stride + nextJ); indices.push_back(i * stride + nextJ); indices.push_back(nextI * stride + j); indices.push_back(nextI * stride + nextJ); } } // Create the primitive object. std::unique_ptr<GeometricPrimitive> primitive(new GeometricPrimitive()); primitive->pImpl->Initialize( vertices, indices, rhcoords); return primitive; }
int main(int argc, char *argv[]) { int test = argc > 1 ? atoi(argv[1]) : 0; int verbose = argc > 2; int veryVerbose = argc > 3; int veryVeryVerbose = argc > 4; cout << "TEST " << __FILE__ << " CASE " << test << endl; switch (test) { case 0: case 2: { // -------------------------------------------------------------------- // USAGE EXAMPLE // Extracted from component header file. // // Concerns: //: 1 The usage example provided in the component header file compiles, //: links, and runs as shown. // // Plan: //: 1 Incorporate usage example from header into test driver, remove //: leading comment characters, and replace 'assert' with 'ASSERT'. //: (C-1) // // Testing: // USAGE EXAMPLE // -------------------------------------------------------------------- if (verbose) cout << endl << "USAGE EXAMPLE" << endl << "=============" << endl; { static const struct { const char *d_name; // name of market index int d_size; // size of the index } DATA[] = { // NAME SIZE // -------------- ---- { "DJIA", 30 }, { "S&P 500", 500 }, { "Russell 1000", 1000 }, }; const int NUM_DATA = sizeof DATA / sizeof *DATA; IndexCollection indices; indices.reserve(NUM_DATA); for (int ti = 0; ti < NUM_DATA; ++ti) { indices.push_back(IndexAttributes(DATA[ti].d_name, DATA[ti].d_size)); } bslma::TestAllocator da("default", veryVeryVerbose); bslma::DefaultAllocatorGuard dag(&da); 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); ASSERT(1 == da.numBlocksTotal()); allocator->deallocate(buffer); } } break; case 1: { // -------------------------------------------------------------------- // PROTOCOL TEST: // Ensure this class is a properly defined protocol. // // Concerns: //: 1 The protocol is abstract: no objects of it can be created. //: //: 2 The protocol has no data members. //: //: 3 The protocol has a virtual destructor. //: //: 4 All methods of the protocol are pure virtual. //: //: 5 All methods of the protocol are publicly accessible. // // Plan: //: 1 Define a concrete derived implementation, 'ProtocolClassTestImp', //: of the protocol. //: //: 2 Create an object of the 'bsls::ProtocolTest' class template //: parameterized by 'ProtocolClassTestImp', and use it to verify //: that: //: //: 1 The protocol is abstract. (C-1) //: //: 2 The protocol has no data members. (C-2) //: //: 3 The protocol has a virtual destructor. (C-3) //: //: 3 Use the 'BSLS_PROTOCOLTEST_ASSERT' macro to verify that //: non-creator methods of the protocol are: //: //: 1 virtual, (C-4) //: //: 2 publicly accessible. (C-5) // // Testing: // virtual void release() = 0; // -------------------------------------------------------------------- if (verbose) cout << endl << "PROTOCOL TEST" << endl << "=============" << endl; if (verbose) cout << "\nCreate a test object.\n"; bsls::ProtocolTest<ProtocolClassTestImp> testObj(veryVerbose); if (verbose) cout << "\nVerify that the protocol is abstract.\n"; ASSERT(testObj.testAbstract()); if (verbose) cout << "\nVerify that there are no data members.\n"; ASSERT(testObj.testNoDataMembers()); if (verbose) cout << "\nVerify that the destructor is virtual.\n"; ASSERT(testObj.testVirtualDestructor()); if (verbose) cout << "\nVerify that methods are public and virtual.\n"; // 'bslma::Allocator' protocol BSLS_PROTOCOLTEST_ASSERT(testObj, allocate(0)); BSLS_PROTOCOLTEST_ASSERT(testObj, deallocate(0)); // 'bdlma::ManagedAllocator' protocol BSLS_PROTOCOLTEST_ASSERT(testObj, release()); } break; default: { cerr << "WARNING: CASE `" << test << "' NOT FOUND." << endl; testStatus = -1; } } if (testStatus > 0) { cerr << "Error, non-zero test status = " << testStatus << "." << endl; } return testStatus; }
//-------------------------------------------------------------------------------------- // Cube (aka a Hexahedron) or Box //-------------------------------------------------------------------------------------- void DirectX::ComputeBox(VertexCollection& vertices, IndexCollection& indices, const XMFLOAT3& size, bool rhcoords, bool invertn) { vertices.clear(); indices.clear(); // A box has six faces, each one pointing in a different direction. const int FaceCount = 6; static const XMVECTORF32 faceNormals[FaceCount] = { { { { 0, 0, 1, 0 } } }, { { { 0, 0, -1, 0 } } }, { { { 1, 0, 0, 0 } } }, { { { -1, 0, 0, 0 } } }, { { { 0, 1, 0, 0 } } }, { { { 0, -1, 0, 0 } } }, }; static const XMVECTORF32 textureCoordinates[4] = { { { { 1, 0, 0, 0 } } }, { { { 1, 1, 0, 0 } } }, { { { 0, 1, 0, 0 } } }, { { { 0, 0, 0, 0 } } }, }; XMVECTOR tsize = XMLoadFloat3(&size); tsize = XMVectorDivide(tsize, g_XMTwo); // Create each face in turn. for (int i = 0; i < FaceCount; i++) { XMVECTOR normal = faceNormals[i]; // Get two vectors perpendicular both to the face normal and to each other. XMVECTOR basis = (i >= 4) ? g_XMIdentityR2 : g_XMIdentityR1; XMVECTOR side1 = XMVector3Cross(normal, basis); XMVECTOR side2 = XMVector3Cross(normal, side1); // Six indices (two triangles) per face. size_t vbase = vertices.size(); index_push_back(indices, vbase + 0); index_push_back(indices, vbase + 1); index_push_back(indices, vbase + 2); index_push_back(indices, vbase + 0); index_push_back(indices, vbase + 2); index_push_back(indices, vbase + 3); // Four vertices per face. // (normal - side1 - side2) * tsize // normal // t0 vertices.push_back(VertexPositionNormalTexture(XMVectorMultiply(XMVectorSubtract(XMVectorSubtract(normal, side1), side2), tsize), normal, textureCoordinates[0])); // (normal - side1 + side2) * tsize // normal // t1 vertices.push_back(VertexPositionNormalTexture(XMVectorMultiply(XMVectorAdd(XMVectorSubtract(normal, side1), side2), tsize), normal, textureCoordinates[1])); // (normal + side1 + side2) * tsize // normal // t2 vertices.push_back(VertexPositionNormalTexture(XMVectorMultiply(XMVectorAdd(normal, XMVectorAdd(side1, side2)), tsize), normal, textureCoordinates[2])); // (normal + side1 - side2) * tsize // normal // t3 vertices.push_back(VertexPositionNormalTexture(XMVectorMultiply(XMVectorSubtract(XMVectorAdd(normal, side1), side2), tsize), normal, textureCoordinates[3])); } // Build RH above if (!rhcoords) ReverseWinding(indices, vertices); if (invertn) InvertNormals(vertices); }
std::vector<plint> findIndexes(IndexCollection const& collection) { return collection.get(); }
std::unique_ptr<GeometricPrimitive> GeometricPrimitive::CreateIcosahedron( float size /*= 1*/, bool rhcoords /*= true*/) { VertexCollection vertices; IndexCollection indices; static const float t = 1.618033988749894848205f; // (1 + sqrt(5)) / 2 static const float t2 = 1.519544995837552493271f; // sqrt( 1 + sqr( (1 + sqrt(5)) / 2 ) ) static const XMVECTORF32 verts[12] = { { t / t2, 1.f / t2, 0 }, { -t / t2, 1.f / t2, 0 }, { t / t2, -1.f / t2, 0 }, { -t / t2, -1.f / t2, 0 }, { 1.f / t2, 0, t / t2 }, { 1.f / t2, 0, -t / t2 }, { -1.f / t2, 0, t / t2 }, { -1.f / t2, 0, -t / t2 }, { 0, t / t2, 1.f / t2 }, { 0, -t / t2, 1.f / t2 }, { 0, t / t2, -1.f / t2 }, { 0, -t / t2, -1.f / t2 } }; static const uint32_t faces[20 * 3] = { 0, 8, 4, 0, 5, 10, 2, 4, 9, 2, 11, 5, 1, 6, 8, 1, 10, 7, 3, 9, 6, 3, 7, 11, 0, 10, 8, 1, 8, 10, 2, 9, 11, 3, 11, 9, 4, 2, 0, 5, 0, 2, 6, 1, 3, 7, 3, 1, 8, 6, 4, 9, 4, 6, 10, 5, 7, 11, 7, 5 }; for (size_t j = 0; j < _countof(faces); j += 3) { uint32_t v0 = faces[j]; uint32_t v1 = faces[j + 1]; uint32_t v2 = faces[j + 2]; XMVECTOR normal = XMVector3Cross(verts[v1].v - verts[v0].v, verts[v2].v - verts[v0].v); normal = XMVector3Normalize(normal); size_t base = vertices.size(); indices.push_back(base); indices.push_back(base + 1); indices.push_back(base + 2); // Duplicate vertices to use face normals XMVECTOR position = XMVectorScale(verts[v0], size); vertices.push_back(VertexPositionNormalTexture(position, normal, g_XMZero /* 0, 0 */)); position = XMVectorScale(verts[v1], size); vertices.push_back(VertexPositionNormalTexture(position, normal, g_XMIdentityR0 /* 1, 0 */)); position = XMVectorScale(verts[v2], size); vertices.push_back(VertexPositionNormalTexture(position, normal, g_XMIdentityR1 /* 0, 1 */)); } assert(vertices.size() == 20 * 3); assert(indices.size() == 20 * 3); // Create the primitive object. std::unique_ptr<GeometricPrimitive> primitive(new GeometricPrimitive()); primitive->pImpl->Initialize( vertices, indices, !rhcoords); return primitive; }
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; }
//-------------------------------------------------------------------------------------- // Sphere //-------------------------------------------------------------------------------------- void DirectX::ComputeSphere(VertexCollection& vertices, IndexCollection& indices, float diameter, size_t tessellation, bool rhcoords, bool invertn) { vertices.clear(); indices.clear(); if (tessellation < 3) throw std::out_of_range("tesselation parameter out of range"); size_t verticalSegments = tessellation; size_t horizontalSegments = tessellation * 2; float radius = diameter / 2; // Create rings of vertices at progressively higher latitudes. for (size_t i = 0; i <= verticalSegments; i++) { float v = 1 - float(i) / verticalSegments; float latitude = (i * XM_PI / verticalSegments) - XM_PIDIV2; float dy, dxz; XMScalarSinCos(&dy, &dxz, latitude); // Create a single ring of vertices at this latitude. for (size_t j = 0; j <= horizontalSegments; j++) { float u = float(j) / horizontalSegments; float longitude = j * XM_2PI / horizontalSegments; float dx, dz; XMScalarSinCos(&dx, &dz, longitude); dx *= dxz; dz *= dxz; XMVECTOR normal = XMVectorSet(dx, dy, dz, 0); XMVECTOR textureCoordinate = XMVectorSet(u, v, 0, 0); vertices.push_back(VertexPositionNormalTexture(XMVectorScale(normal, radius), normal, textureCoordinate)); } } // Fill the index buffer with triangles joining each pair of latitude rings. size_t stride = horizontalSegments + 1; for (size_t i = 0; i < verticalSegments; i++) { for (size_t j = 0; j <= horizontalSegments; j++) { size_t nextI = i + 1; size_t nextJ = (j + 1) % stride; index_push_back(indices, i * stride + j); index_push_back(indices, nextI * stride + j); index_push_back(indices, i * stride + nextJ); index_push_back(indices, i * stride + nextJ); index_push_back(indices, nextI * stride + j); index_push_back(indices, nextI * stride + nextJ); } } // Build RH above if (!rhcoords) ReverseWinding(indices, vertices); if (invertn) InvertNormals(vertices); }
std::unique_ptr<GeometricPrimitive> GeometricPrimitive::CreateCube( float size /*= 1*/, bool rhcoords /*= true*/) { // A cube has six faces, each one pointing in a different direction. const int FaceCount = 6; static const XMVECTORF32 faceNormals[FaceCount] = { { 0, 0, 1 }, { 0, 0, -1 }, { 1, 0, 0 }, { -1, 0, 0 }, { 0, 1, 0 }, { 0, -1, 0 }, }; static const XMVECTORF32 textureCoordinates[4] = { { 1, 0 }, { 1, 1 }, { 0, 1 }, { 0, 0 }, }; VertexCollection vertices; IndexCollection indices; size /= 2; // Create each face in turn. for (int i = 0; i < FaceCount; i++) { XMVECTOR normal = faceNormals[i]; // Get two vectors perpendicular both to the face normal and to each other. XMVECTOR basis = (i >= 4) ? g_XMIdentityR2 : g_XMIdentityR1; XMVECTOR side1 = XMVector3Cross(normal, basis); XMVECTOR side2 = XMVector3Cross(normal, side1); // Six indices (two triangles) per face. size_t vbase = vertices.size(); indices.push_back(vbase + 0); indices.push_back(vbase + 1); indices.push_back(vbase + 2); indices.push_back(vbase + 0); indices.push_back(vbase + 2); indices.push_back(vbase + 3); // Four vertices per face. vertices.push_back(VertexPositionNormalTexture((normal - side1 - side2) * size, normal, textureCoordinates[0])); vertices.push_back(VertexPositionNormalTexture((normal - side1 + side2) * size, normal, textureCoordinates[1])); vertices.push_back(VertexPositionNormalTexture((normal + side1 + side2) * size, normal, textureCoordinates[2])); vertices.push_back(VertexPositionNormalTexture((normal + side1 - side2) * size, normal, textureCoordinates[3])); } // Create the primitive object. std::unique_ptr<GeometricPrimitive> primitive(new GeometricPrimitive()); primitive->pImpl->Initialize( vertices, indices, rhcoords); return primitive; }
//-------------------------------------------------------------------------------------- // Dodecahedron //-------------------------------------------------------------------------------------- void DirectX::ComputeDodecahedron(VertexCollection& vertices, IndexCollection& indices, float size, bool rhcoords) { vertices.clear(); indices.clear(); static const float a = 1.f / SQRT3; static const float b = 0.356822089773089931942f; // sqrt( ( 3 - sqrt(5) ) / 6 ) static const float c = 0.934172358962715696451f; // sqrt( ( 3 + sqrt(5) ) / 6 ); static const XMVECTORF32 verts[20] = { { { { a, a, a, 0 } } }, { { { a, a, -a, 0 } } }, { { { a, -a, a, 0 } } }, { { { a, -a, -a, 0 } } }, { { { -a, a, a, 0 } } }, { { { -a, a, -a, 0 } } }, { { { -a, -a, a, 0 } } }, { { { -a, -a, -a, 0 } } }, { { { b, c, 0, 0 } } }, { { { -b, c, 0, 0 } } }, { { { b, -c, 0, 0 } } }, { { { -b, -c, 0, 0 } } }, { { { c, 0, b, 0 } } }, { { { c, 0, -b, 0 } } }, { { { -c, 0, b, 0 } } }, { { { -c, 0, -b, 0 } } }, { { { 0, b, c, 0 } } }, { { { 0, -b, c, 0 } } }, { { { 0, b, -c, 0 } } }, { { { 0, -b, -c, 0 } } } }; static const uint32_t faces[12 * 5] = { 0, 8, 9, 4, 16, 0, 16, 17, 2, 12, 12, 2, 10, 3, 13, 9, 5, 15, 14, 4, 3, 19, 18, 1, 13, 7, 11, 6, 14, 15, 0, 12, 13, 1, 8, 8, 1, 18, 5, 9, 16, 4, 14, 6, 17, 6, 11, 10, 2, 17, 7, 15, 5, 18, 19, 7, 19, 3, 10, 11, }; static const XMVECTORF32 textureCoordinates[5] = { { { { 0.654508f, 0.0244717f, 0, 0 } } }, { { { 0.0954915f, 0.206107f, 0, 0 } } }, { { { 0.0954915f, 0.793893f, 0, 0 } } }, { { { 0.654508f, 0.975528f, 0, 0 } } }, { { { 1.f, 0.5f, 0, 0 } } } }; static const uint32_t textureIndex[12][5] = { { 0, 1, 2, 3, 4 }, { 2, 3, 4, 0, 1 }, { 4, 0, 1, 2, 3 }, { 1, 2, 3, 4, 0 }, { 2, 3, 4, 0, 1 }, { 0, 1, 2, 3, 4 }, { 1, 2, 3, 4, 0 }, { 4, 0, 1, 2, 3 }, { 4, 0, 1, 2, 3 }, { 1, 2, 3, 4, 0 }, { 0, 1, 2, 3, 4 }, { 2, 3, 4, 0, 1 }, }; size_t t = 0; for (size_t j = 0; j < _countof(faces); j += 5, ++t) { uint32_t v0 = faces[j]; uint32_t v1 = faces[j + 1]; uint32_t v2 = faces[j + 2]; uint32_t v3 = faces[j + 3]; uint32_t v4 = faces[j + 4]; XMVECTOR normal = XMVector3Cross( XMVectorSubtract(verts[v1].v, verts[v0].v), XMVectorSubtract(verts[v2].v, verts[v0].v)); normal = XMVector3Normalize(normal); size_t base = vertices.size(); index_push_back(indices, base); index_push_back(indices, base + 1); index_push_back(indices, base + 2); index_push_back(indices, base); index_push_back(indices, base + 2); index_push_back(indices, base + 3); index_push_back(indices, base); index_push_back(indices, base + 3); index_push_back(indices, base + 4); // Duplicate vertices to use face normals XMVECTOR position = XMVectorScale(verts[v0], size); vertices.push_back(VertexPositionNormalTexture(position, normal, textureCoordinates[textureIndex[t][0]])); position = XMVectorScale(verts[v1], size); vertices.push_back(VertexPositionNormalTexture(position, normal, textureCoordinates[textureIndex[t][1]])); position = XMVectorScale(verts[v2], size); vertices.push_back(VertexPositionNormalTexture(position, normal, textureCoordinates[textureIndex[t][2]])); position = XMVectorScale(verts[v3], size); vertices.push_back(VertexPositionNormalTexture(position, normal, textureCoordinates[textureIndex[t][3]])); position = XMVectorScale(verts[v4], size); vertices.push_back(VertexPositionNormalTexture(position, normal, textureCoordinates[textureIndex[t][4]])); } // Built LH above if (rhcoords) ReverseWinding(indices, vertices); assert(vertices.size() == 12 * 5); assert(indices.size() == 12 * 3 * 3); }