=== OBJ 파일과 BVH 를 로딩하는 샘플 코드 {{{#!cpp lineno=1 #pragma mark Mesh Loading ModelBvh LoadObjModel(const DKString& obj, const DKString& tex, DKMaterial* material, bool genBvh, bool useCachedData) { ModelBvh result = { "", NULL, NULL }; DKResourcePool pool; DKObject texture = pool.LoadResource(tex).SafeCast(); if (texture == NULL) { DKLog("Failed to load texture: %ls\n", (const wchar_t*)tex); return result; } DKString dkMeshFile = obj + ".dkmesh"; DKString dkBvhFile = obj + ".dktbvh"; DKObject mesh = NULL; DKObject bvh = NULL; if (useCachedData) { mesh = pool.LoadResource(dkMeshFile).SafeCast(); if (mesh && genBvh) { DKObject triData = pool.LoadResourceData(dkBvhFile); if (triData && triData->Length() > 0) { DKObject bvhMesh = DKOBJECT_NEW BvhMesh(); const DKTriangle* triangles = reinterpret_cast(triData->LockShared()); int numTri = (int)(triData->Length() / sizeof(DKTriangle)); bvhMesh->triangles.Add(triangles, numTri); triData->UnlockShared(); bvh = DKOBJECT_NEW DKTriangleMeshBvh(); bvh->Build(bvhMesh); } } } if (mesh == NULL || (genBvh && bvh == NULL)) { DKObject objData = pool.LoadResourceData(obj); if (objData && objData->Length() > 0) { DKLog("Loading %ls... (%lu bytes)\n", (const wchar_t*)obj, objData->Length()); DKTimer timer; timer.Reset(); DKArray positions; DKArray texcoords; DKArray normals; struct Face { int pos, uv, normal; }; DKArray faces; DKString lineString; positions.Reserve(0x10000); texcoords.Reserve(0x10000); normals.Reserve(0x10000); faces.Reserve(0x10000); DKUniChar8* p = (DKUniChar8*)objData->LockShared(); size_t length = objData->Length(); size_t pos = 0; for (size_t i = 0; i < length; ++i) { if (p[i] == '\n') { if (pos < i) { DKString::StringArray words = DKString(&p[pos], (i - pos)).SplitByWhitespace(); if (words.Count() > 0) { if (words.Value(0).CompareNoCase("v") == 0) // vertex { if (words.Count() > 3) { DKVector3 vector(words.Value(1).ToRealNumber(), words.Value(2).ToRealNumber(), words.Value(3).ToRealNumber()); positions.Add(vector); } } else if (words.Value(0).CompareNoCase("vn") == 0) // normal { if (words.Count() > 3) { DKVector3 vector(words.Value(1).ToRealNumber(), words.Value(2).ToRealNumber(), words.Value(3).ToRealNumber()); normals.Add(vector); } } else if (words.Value(0).CompareNoCase("vt") == 0) // tex-coords { if (words.Count() > 2) { DKVector2 vector(words.Value(1).ToRealNumber(), words.Value(2).ToRealNumber()); texcoords.Add(vector); } } else if (words.Value(0).CompareNoCase("f") == 0) // face { size_t numFaces = words.Count() - 1; Face f[4]; int idx = 0; auto getIndex = [](int idx, int count) -> int { int v; if (idx > 0) v = idx - 1; else v = count + idx; return Clamp(v, 0, count - 1); }; while (idx < numFaces) { DKString::IntegerArray indices = words.Value(idx + 1).ToIntegerArray("/"); if (indices.Count() > 2) // pos, uv, normal { f[idx].pos = getIndex(indices.Value(0), positions.Count()); f[idx].uv = getIndex(indices.Value(1), texcoords.Count()); f[idx].normal = getIndex(indices.Value(2), normals.Count()); idx++; } else if (indices.Count() > 1) // pos, uv { f[idx].pos = getIndex(indices.Value(0), positions.Count()); f[idx].uv = getIndex(indices.Value(1), texcoords.Count()); f[idx].normal = 0; idx++; } else break; } if (idx == 3) // triangle { faces.Add(f, 3); } else if (idx == 4) // quad { faces.Add({ f[1], f[2], f[0], f[0], f[2], f[3] }); } else // error?? { } } } } pos = i + 1; } } objData->UnlockShared(); objData = NULL; DKLog("Num Positions: %lu\n", positions.Count()); DKLog("Num TexCoords: %lu\n", texcoords.Count()); DKLog("Num Normals: %lu\n", normals.Count()); DKLog("Num Faces: %lu\n", faces.Count()); if (faces.Count() > 0 && positions.Count() > 0 && texcoords.Count() > 0) { struct Vertex { DKVector3 pos; DKVector3 normal; DKVector2 uv; }; DKAabb aabb; DKArray vertices; vertices.Reserve(faces.Count()); if (normals.Count() > 0) { for (int i = 0; (i + 2) < faces.Count(); i += 3) { const Face& f1 = faces.Value(i); const Face& f2 = faces.Value(i + 1); const Face& f3 = faces.Value(i + 2); Vertex v[3] = { { positions.Value(f1.pos), normals.Value(f1.normal), texcoords.Value(f1.uv) }, { positions.Value(f2.pos), normals.Value(f2.normal), texcoords.Value(f2.uv) }, { positions.Value(f3.pos), normals.Value(f2.normal), texcoords.Value(f3.uv) }, }; vertices.Add(v, 3); aabb.Expand(v[0].pos); aabb.Expand(v[1].pos); aabb.Expand(v[2].pos); } } else { DKLog("Generating face normals...\n"); for (int i = 0; (i + 2) < faces.Count(); i += 3) { const Face& f1 = faces.Value(i); const Face& f2 = faces.Value(i + 1); const Face& f3 = faces.Value(i + 2); const DKVector3& pos1 = positions.Value(f1.pos); const DKVector3& pos2 = positions.Value(f2.pos); const DKVector3& pos3 = positions.Value(f3.pos); DKVector3 faceNormal = DKVector3::Cross(pos2 - pos1, pos3 - pos1).Normalize(); Vertex v[3] = { { pos1, faceNormal, texcoords.Value(f1.uv) }, { pos2, faceNormal, texcoords.Value(f2.uv) }, { pos3, faceNormal, texcoords.Value(f3.uv) }, }; vertices.Add(v, 3); aabb.Expand(v[0].pos); aabb.Expand(v[1].pos); aabb.Expand(v[2].pos); } } DKVertexBuffer::Decl decls[3] = { { DKVertexStream::StreamPosition, L"", DKVertexStream::TypeFloat3, false }, { DKVertexStream::StreamNormal , L"", DKVertexStream::TypeFloat3, false }, { DKVertexStream::StreamTexCoord, L"", DKVertexStream::TypeFloat2, false }, }; DKObject vb = DKVertexBuffer::Create(decls, 3, vertices, sizeof(Vertex), vertices.Count(), DKVertexBuffer::MemoryLocationStatic, DKVertexBuffer::BufferUsageDraw); mesh = DKObject::New(); mesh->SetDrawFace(DKMesh::DrawFaceBoth); mesh->SetDefaultPrimitiveType(DKPrimitive::TypeTriangles); mesh->AddVertexBuffer(vb); mesh->SetAabb(aabb); DKLog("Obj %ls file loaded. (%f sec)\n", (const wchar_t*)obj, timer.Elapsed()); DKObject bvhMesh = NULL; if (genBvh) { // Create bvh DKTimer bvhTimer; bvhTimer.Reset(); bvhMesh = DKOBJECT_NEW BvhMesh(); int numTri = (int)(vertices.Count() / 3); bvhMesh->triangles.Reserve(numTri); for (int i = 0; i < numTri; ++i) { const Vertex& v1 = vertices.Value(i * 3); const Vertex& v2 = vertices.Value(i * 3 + 1); const Vertex& v3 = vertices.Value(i * 3 + 2); DKTriangle t = { v1.pos, v2.pos, v3.pos }; bvhMesh->triangles.Add(t); } bvh = DKOBJECT_NEW DKTriangleMeshBvh(); bvh->Build(bvhMesh); double d = bvhTimer.Elapsed(); DKLog("Generating BVH elapsed: %f\n", d); } if (useCachedData) { DKObject data = mesh->Serialize(DKSerializer::SerializeFormCompressedBinary); if (data) { DKLog("Writing DKMesh file: %ls\n", (const wchar_t*)dkMeshFile); data->WriteToFile(dkMeshFile, true); } if (bvhMesh && bvhMesh->triangles.Count() > 0) { data = DKData::StaticData(bvhMesh->triangles, sizeof(DKTriangle) * bvhMesh->triangles.Count()); if (data) { DKLog("Writing DKBvh file: %ls\n", (const wchar_t*)dkBvhFile); data->WriteToFile(dkBvhFile, true); } } } } } else { // error. DKLog("Failed to load obj file: %ls\n", (const wchar_t*)obj); } } if (mesh) { DKAabb aabb = mesh->Aabb(); DKLog("AABB: (%.3f, %.3f, %.3f), (%.3f, %.3f, %.3f)\n", aabb.positionMin.x, aabb.positionMin.y, aabb.positionMin.z, aabb.positionMax.x, aabb.positionMax.y, aabb.positionMax.z); DKLog("AABB width: %.3f, height: %.3f, depth: %.3f\n", aabb.positionMax.x - aabb.positionMin.x, aabb.positionMax.y - aabb.positionMin.y, aabb.positionMax.z - aabb.positionMin.z); // DKObject sampler = DKOBJECT_NEW DKTextureSampler(); // sampler->magFilter = DKTextureSampler::MagFilterNearest; // sampler->minFilter = DKTextureSampler::MinFilterNearest; mesh->SetMaterial(material); mesh->SetSampler(L"diffuseMap", DKMaterial::TextureArray(texture.SafeCast(), 1), NULL); result.mesh = mesh; result.bvh = bvh; } else { DKLog("Load %ls failed.\n", (const wchar_t*)obj); } return result; } }}} - DKGL develop 브랜치 버전용