diff options
| author | Yong He <yonghe@outlook.com> | 2021-01-06 12:58:57 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-01-06 12:58:57 -0800 |
| commit | 92636513abe72d2da0c45f0e2c1235415e0671c3 (patch) | |
| tree | 234136e9d89006df9d6775e8bcd07e91ae344af7 /tools/gfx/model.cpp | |
| parent | 706d4f91e269d473c963d31792fb2c8320933c9b (diff) | |
Refactor GUI/Window utils out of gfx library (#1649)
Co-authored-by: Yong He <yhe@nvidia.com>
Diffstat (limited to 'tools/gfx/model.cpp')
| -rw-r--r-- | tools/gfx/model.cpp | 564 |
1 files changed, 0 insertions, 564 deletions
diff --git a/tools/gfx/model.cpp b/tools/gfx/model.cpp deleted file mode 100644 index ce176727b..000000000 --- a/tools/gfx/model.cpp +++ /dev/null @@ -1,564 +0,0 @@ -// model.cpp -#include "model.h" - -#define TINYOBJLOADER_IMPLEMENTATION -#include "../../external/tinyobjloader/tiny_obj_loader.h" - -#define STB_IMAGE_IMPLEMENTATION -#include "../../external/stb/stb_image.h" - -#define STB_IMAGE_RESIZE_IMPLEMENTATION -#include "../../external/stb/stb_image_resize.h" - -#include "../../external/glm/glm/glm.hpp" -#include "../../external/glm/glm/gtc/matrix_transform.hpp" -#include "../../external/glm/glm/gtc/constants.hpp" - -#include <memory> -#include <unordered_map> -#include <unordered_set> - -namespace gfx { - -// TinyObj provides a tuple type that bundles up indices, but doesn't -// provide equality comparison or hashing for that type. We'd like -// to have a hash function so that we can unique indices. -// -// In the simplest case, we could define hashing and operator== operations -// directly on `tinobj::index_t`, but that would create problems if they -// revise their API. -// -// We will instead define our own wrapper type that supports equality -// comparisons. -// -struct ObjIndexKey -{ - tinyobj::index_t index; -}; - -bool operator==(ObjIndexKey const& left, ObjIndexKey const& right) -{ - return left.index.vertex_index == right.index.vertex_index - && left.index.normal_index == right.index.normal_index - && left.index.texcoord_index == right.index.texcoord_index; -} - -struct Hasher -{ - template<typename T> - void add(T const& v) - { - state ^= std::hash<T>()(v) + 0x9e3779b9 + (state << 6) + (state >> 2); - } - size_t state = 0; -}; - -struct SmoothingGroupVertexID -{ - size_t smoothingGroup; - size_t positionID; -}; -bool operator==(SmoothingGroupVertexID const& left, SmoothingGroupVertexID const& right) -{ - return left.smoothingGroup == right.smoothingGroup - && left.positionID == right.positionID; -} - -} - -namespace std -{ - template<> struct hash<gfx::ObjIndexKey> - { - size_t operator()(gfx::ObjIndexKey const& key) const - { - gfx::Hasher hasher; - hasher.add(key.index.vertex_index); - hasher.add(key.index.normal_index); - hasher.add(key.index.texcoord_index); - return hasher.state; - } - }; - - template<> struct hash<gfx::SmoothingGroupVertexID> - { - size_t operator()(gfx::SmoothingGroupVertexID const& id) const - { - gfx::Hasher hasher; - hasher.add(id.smoothingGroup); - hasher.add(id.positionID); - return hasher.state; - } - }; -} - -namespace gfx -{ - -RefPtr<TextureResource> loadTextureImage( - Renderer* renderer, - char const* path) -{ - int extentX = 0; - int extentY = 0; - int originalChannelCount = 0; - int requestedChannelCount = 4; // force to 4-component result - stbi_uc* data = stbi_load( - path, - &extentX, - &extentY, - &originalChannelCount, - requestedChannelCount); - if(!data) - return nullptr; - - int channelCount = requestedChannelCount ? requestedChannelCount : originalChannelCount; - - Format format; - switch(channelCount) - { - default: - return nullptr; - - case 4: format = Format::RGBA_Unorm_UInt8; - - // TODO: handle other cases here if/when we stop forcing 4-component - // results when loading the image with stb_image. - } - - std::vector<void*> subresourceInitData; - std::vector<ptrdiff_t> mipRowStrides; - - ptrdiff_t stride = extentX * channelCount * sizeof(stbi_uc); - - subresourceInitData.push_back(data); - mipRowStrides.push_back(stride); - - // create down-sampled images for the different mip levels - bool generateMips = true; - if(generateMips) - { - int prevExtentX = extentX; - int prevExtentY = extentY; - stbi_uc* prevData = data; - int prevStride = int(stride); - - for(;;) - { - if(prevExtentX == 1 && prevExtentY == 1) - break; - - int newExtentX = prevExtentX / 2; - int newExtentY = prevExtentY / 2; - - if(!newExtentX) newExtentX = 1; - if(!newExtentY) newExtentY = 1; - - stbi_uc* newData = (stbi_uc*) malloc(newExtentX * newExtentY * channelCount * sizeof(stbi_uc)); - int newStride = int(newExtentX * channelCount * sizeof(stbi_uc)); - - stbir_resize_uint8_srgb( - prevData, prevExtentX, prevExtentY, prevStride, - newData, newExtentX, newExtentY, newStride, - channelCount, - STBIR_ALPHA_CHANNEL_NONE, - STBIR_FLAG_ALPHA_PREMULTIPLIED); - - subresourceInitData.push_back(newData); - mipRowStrides.push_back(newStride); - - prevExtentX = newExtentX; - prevExtentY = newExtentY; - prevData = newData; - prevStride = newStride; - } - } - - int mipCount = (int) mipRowStrides.size(); - - TextureResource::Desc desc; - desc.init2D(Resource::Type::Texture2D, format, extentX, extentY, mipCount); - - TextureResource::Data initData; - initData.numSubResources = mipCount; - initData.numMips = mipCount; - initData.subResources = &subresourceInitData[0]; - initData.mipRowStrides = &mipRowStrides[0]; - - auto texture = renderer->createTextureResource( - Resource::Usage::PixelShaderResource, - desc, - &initData); - - free(data); - - return texture; -} - -static std::string makeString(const char* start, const char* end) -{ - return std::string(start, size_t(end - start)); -} - -Result ModelLoader::load( - char const* inputPath, - void** outModel) -{ - // TODO: need to actually allocate/load the data - - tinyobj::attrib_t objVertexAttributes; - std::vector<tinyobj::shape_t> objShapes; - std::vector<tinyobj::material_t> objMaterials; - - std::string baseDir; - if( auto lastSlash = strrchr(inputPath, '/') ) - { - baseDir = makeString(inputPath, lastSlash); - } - - std::string diagnostics; - bool shouldTriangulate = true; - bool success = tinyobj::LoadObj( - &objVertexAttributes, - &objShapes, - &objMaterials, - &diagnostics, - inputPath, - baseDir.size() ? baseDir.c_str() : nullptr, - shouldTriangulate); - - if(!diagnostics.empty()) - { - log("%s", diagnostics.c_str()); - } - if(!success) - { - return SLANG_FAIL; - } - - // Translate each material imported by TinyObj into a format that - // we can actually use for rendering. - // - std::vector<void*> materials; - for(auto& objMaterial : objMaterials) - { - MaterialData materialData; - - materialData.diffuseColor = glm::vec3( - objMaterial.diffuse[0], - objMaterial.diffuse[1], - objMaterial.diffuse[2]); - - materialData.specularColor = glm::vec3( - objMaterial.specular[0], - objMaterial.specular[1], - objMaterial.specular[2]); - - materialData.specularity = objMaterial.shininess; - - // load any referenced textures here - if(objMaterial.diffuse_texname.length()) - { - materialData.diffuseMap = loadTextureImage( - renderer, - objMaterial.diffuse_texname.c_str()); - } - - auto material = callbacks->createMaterial(materialData); - materials.push_back(material); - } - - // Flip the winding order on all faces if we are asked to... - // - if(loadFlags & LoadFlag::FlipWinding) - { - for(auto& objShape : objShapes) - { - size_t objIndexCounter = 0; - size_t objFaceCounter = 0; - for(auto objFaceVertexCount : objShape.mesh.num_face_vertices) - { - size_t beginIndex = objIndexCounter; - size_t endIndex = beginIndex + objFaceVertexCount; - objIndexCounter = endIndex; - - size_t halfCount = objFaceVertexCount / 2; - for(size_t ii = 0; ii < halfCount; ++ii) - { - std::swap( - objShape.mesh.indices[beginIndex + ii], - objShape.mesh.indices[endIndex - (ii + 1)]); - } - } - } - - } - - // Identify cases where a face has a vertex without a normal, and in that - // case remember that the given vertex needs to be "smoothed" as part of - // the smoothing group for that face. Note that it is possible for the - // same vertex (position) to be part of faces in distinct smoothing groups. - // - std::unordered_map<SmoothingGroupVertexID, size_t> smoothedVertexNormals; - size_t firstSmoothedNormalID = objVertexAttributes.normals.size() / 3; - size_t flatFaceCounter = 0; - for(auto& objShape : objShapes) - { - size_t objIndexCounter = 0; - size_t objFaceCounter = 0; - for(auto objFaceVertexCount : objShape.mesh.num_face_vertices) - { - const size_t flatFaceIndex = flatFaceCounter++; - const size_t objFaceIndex = objFaceCounter++; - size_t smoothingGroup = objShape.mesh.smoothing_group_ids[objFaceIndex]; - if(!smoothingGroup) - { - smoothingGroup = ~flatFaceIndex; - } - - for(size_t objFaceVertex = 0; objFaceVertex < objFaceVertexCount; ++objFaceVertex) - { - tinyobj::index_t& objIndex = objShape.mesh.indices[objIndexCounter++]; - - if(objIndex.normal_index < 0) - { - SmoothingGroupVertexID smoothVertexID; - smoothVertexID.positionID = objIndex.vertex_index; - smoothVertexID.smoothingGroup = smoothingGroup; - - if(smoothedVertexNormals.find(smoothVertexID) == smoothedVertexNormals.end()) - { - size_t normalID = objVertexAttributes.normals.size() / 3; - objVertexAttributes.normals.push_back(0); - objVertexAttributes.normals.push_back(0); - objVertexAttributes.normals.push_back(0); - - smoothedVertexNormals.insert(std::make_pair(smoothVertexID, normalID)); - - objIndex.normal_index = int(normalID); - } - } - } - } - } - // - // Having identified which vertices we need to smooth, we will make another - // pass to compute face normals and apply them to the vertices that belong - // to the same smoothing group. - // - flatFaceCounter = 0; - for(auto& objShape : objShapes) - { - size_t objIndexCounter = 0; - size_t objFaceCounter = 0; - for(auto objFaceVertexCount : objShape.mesh.num_face_vertices) - { - const size_t flatFaceIndex = flatFaceCounter++; - const size_t objFaceIndex = objFaceCounter++; - size_t smoothingGroup = objShape.mesh.smoothing_group_ids[objFaceIndex]; - if(!smoothingGroup) - { - smoothingGroup = ~flatFaceIndex; - } - - glm::vec3 faceNormal; - if(objFaceVertexCount >= 3) - { - glm::vec3 v[3]; - for(size_t objFaceVertex = 0; objFaceVertex < 3; ++objFaceVertex) - { - tinyobj::index_t objIndex = objShape.mesh.indices[objIndexCounter + objFaceVertex]; - if(objIndex.vertex_index >= 0) - { - v[objFaceVertex] = glm::vec3( - objVertexAttributes.vertices[3 * objIndex.vertex_index + 0], - objVertexAttributes.vertices[3 * objIndex.vertex_index + 1], - objVertexAttributes.vertices[3 * objIndex.vertex_index + 2]); - } - } - faceNormal = cross(v[1] - v[0], v[2] - v[0]); - } - - // Add this face normal to any to-be-smoothed vertex on the face. - for(size_t objFaceVertex = 0; objFaceVertex < objFaceVertexCount; ++objFaceVertex) - { - tinyobj::index_t objIndex = objShape.mesh.indices[objIndexCounter++]; - - SmoothingGroupVertexID smoothVertexID; - smoothVertexID.positionID = objIndex.vertex_index; - smoothVertexID.smoothingGroup = smoothingGroup; - - auto ii = smoothedVertexNormals.find(smoothVertexID); - if(ii != smoothedVertexNormals.end()) - { - size_t normalID = ii->second; - objVertexAttributes.normals[normalID * 3 + 0] += faceNormal.x; - objVertexAttributes.normals[normalID * 3 + 1] += faceNormal.y; - objVertexAttributes.normals[normalID * 3 + 2] += faceNormal.z; - } - } - } - } - // - // Once we've added all contributions from each smoothing group, - // we can normalize the normals to compute the area-weighted average. - // - size_t normalCount = objVertexAttributes.normals.size() / 3; - for(size_t ii = firstSmoothedNormalID; ii < normalCount; ++ii) - { - glm::vec3 normal = glm::vec3( - objVertexAttributes.normals[3 * ii + 0], - objVertexAttributes.normals[3 * ii + 1], - objVertexAttributes.normals[3 * ii + 2]); - - normal = normalize(normal); - - objVertexAttributes.normals[3 * ii + 0] = normal.x; - objVertexAttributes.normals[3 * ii + 1] = normal.y; - objVertexAttributes.normals[3 * ii + 2] = normal.z; - } - - // TODO: we should sort the faces to group faces with - // the same material ID together, in case they weren't - // grouped in the original file. - - // We need to undo the .obj indexing stuff so that we have - // standard position/normal/etc. data in a single flat array - - std::unordered_map<ObjIndexKey, Index> mapObjIndexToFlatIndex; - std::vector<Vertex> flatVertices; - std::vector<Index> flatIndices; - - MeshData* currentMesh = nullptr; - MeshData currentMeshStorage; - - std::vector<void*> meshes; - - void* defaultMaterial = nullptr; - - for(auto& objShape : objShapes) - { - size_t objIndexCounter = 0; - size_t objFaceCounter = 0; - for(auto objFaceVertexCount : objShape.mesh.num_face_vertices) - { - size_t objFaceIndex = objFaceCounter++; - int faceMaterialID = objShape.mesh.material_ids[objFaceIndex]; - void* faceMaterial = nullptr; - if( faceMaterialID < 0 ) - { - if( !defaultMaterial ) - { - MaterialData defaultMaterialData; - defaultMaterialData.diffuseColor = glm::vec3(0.5, 0.5, 0.5); - defaultMaterial = callbacks->createMaterial(defaultMaterialData); - } - faceMaterial = defaultMaterial; - } - else - { - faceMaterial = materials[faceMaterialID]; - } - - if(!currentMesh || (faceMaterial != currentMesh->material)) - { - // finish old mesh. - if(currentMesh) - { - meshes.push_back(callbacks->createMesh(*currentMesh)); - } - - // Need to start a new mesh. - currentMesh = ¤tMeshStorage; - currentMesh->material = faceMaterial; - currentMesh->firstIndex = (int)flatIndices.size(); - currentMesh->indexCount = 0; - } - - for(size_t objFaceVertex = 0; objFaceVertex < objFaceVertexCount; ++objFaceVertex) - { - tinyobj::index_t objIndex = objShape.mesh.indices[objIndexCounter++]; - ObjIndexKey objIndexKey; objIndexKey.index = objIndex; - - - Index flatIndex = Index(-1); - auto iter = mapObjIndexToFlatIndex.find(objIndexKey); - if(iter != mapObjIndexToFlatIndex.end()) - { - flatIndex = iter->second; - } - else - { - Vertex flatVertex; - if(objIndex.vertex_index >= 0) - { - flatVertex.position = scale * glm::vec3( - objVertexAttributes.vertices[3 * objIndex.vertex_index + 0], - objVertexAttributes.vertices[3 * objIndex.vertex_index + 1], - objVertexAttributes.vertices[3 * objIndex.vertex_index + 2]); - } - if(objIndex.normal_index >= 0) - { - flatVertex.normal = glm::vec3( - objVertexAttributes.normals[3 * objIndex.normal_index + 0], - objVertexAttributes.normals[3 * objIndex.normal_index + 1], - objVertexAttributes.normals[3 * objIndex.normal_index + 2]); - } - if(objIndex.texcoord_index >= 0) - { - flatVertex.uv = glm::vec2( - objVertexAttributes.texcoords[2 * objIndex.texcoord_index + 0], - objVertexAttributes.texcoords[2 * objIndex.texcoord_index + 1]); - } - - flatIndex = uint32_t(flatVertices.size()); - mapObjIndexToFlatIndex.insert(std::make_pair(objIndexKey, flatIndex)); - flatVertices.push_back(flatVertex); - } - - flatIndices.push_back(flatIndex); - currentMesh->indexCount++; - } - } - } - - // finish last mesh. - if(currentMesh) - { - meshes.push_back(callbacks->createMesh(*currentMesh)); - } - - ModelData modelData; - - modelData.vertexCount = (int)flatVertices.size(); - modelData.indexCount = (int)flatIndices.size(); - - modelData.meshCount = int(meshes.size()); - modelData.meshes = meshes.data(); - - BufferResource::Desc vertexBufferDesc; - vertexBufferDesc.init(modelData.vertexCount * sizeof(Vertex)); - vertexBufferDesc.setDefaults(Resource::Usage::VertexBuffer); - - modelData.vertexBuffer = renderer->createBufferResource( - Resource::Usage::VertexBuffer, - vertexBufferDesc, - flatVertices.data()); - if(!modelData.vertexBuffer) return SLANG_FAIL; - - BufferResource::Desc indexBufferDesc; - indexBufferDesc.init(modelData.indexCount * sizeof(Index)); - vertexBufferDesc.setDefaults(Resource::Usage::IndexBuffer); - - modelData.indexBuffer = renderer->createBufferResource( - Resource::Usage::IndexBuffer, - indexBufferDesc, - flatIndices.data()); - if(!modelData.indexBuffer) return SLANG_FAIL; - - *outModel = callbacks->createModel(modelData); - - return SLANG_OK; -} - -} // gfx |
