15class VPKAssimpStream :
public Assimp::IOStream {
17 std::vector<char> m_buffer;
21 VPKAssimpStream(
const char* data,
size_t size)
22 : m_buffer(data, data + size), m_position(0) {}
24 ~VPKAssimpStream()
override =
default;
26 size_t Read(
void* pvBuffer,
size_t pSize,
size_t pCount)
override {
27 size_t bytesToRead = pSize * pCount;
28 size_t bytesAvailable = m_buffer.size() - m_position;
30 if (bytesToRead > bytesAvailable) {
31 bytesToRead = bytesAvailable;
34 if (bytesToRead > 0) {
35 memcpy(pvBuffer, m_buffer.data() + m_position, bytesToRead);
36 m_position += bytesToRead;
39 return bytesToRead / pSize;
42 size_t Write(
const void* pvBuffer,
size_t pSize,
size_t pCount)
override {
46 aiReturn Seek(
size_t pOffset, aiOrigin pOrigin)
override {
47 size_t newPosition = m_position;
50 case aiOrigin_SET: newPosition = pOffset;
break;
51 case aiOrigin_CUR: newPosition = m_position + pOffset;
break;
52 case aiOrigin_END: newPosition = m_buffer.size() + pOffset;
break;
53 default:
return aiReturn_FAILURE;
56 if (newPosition > m_buffer.size()) {
57 return aiReturn_FAILURE;
60 m_position = newPosition;
61 return aiReturn_SUCCESS;
64 size_t Tell()
const override {
68 size_t FileSize()
const override {
69 return m_buffer.size();
72 void Flush()
override {}
76class VPKAssimpIOSystem :
public Assimp::IOSystem {
79 std::string m_basePath;
80 std::string m_baseDir;
84 : m_vfs(vfs), m_basePath(base_path) {
86 std::filesystem::path pathObj(base_path);
87 m_baseDir = pathObj.parent_path().string();
88 if (!m_baseDir.empty() && m_baseDir.back() !=
'/') {
93 ~VPKAssimpIOSystem()
override =
default;
95 bool Exists(
const char* pFile)
const override {
96 std::string filePath(pFile);
98 log(
"Assimp checking if file exists: '%s'", filePath.c_str());
101 if (m_vfs->file_exists(filePath)) {
102 log(
"File exists as-is: '%s'", filePath.c_str());
107 std::string relativePath = m_baseDir + filePath;
108 if (m_vfs->file_exists(relativePath)) {
109 log(
"File exists relative to base: '%s'", relativePath.c_str());
114 std::filesystem::path pathObj(filePath);
115 std::string justFilename = pathObj.filename().string();
116 if (m_vfs->file_exists(justFilename)) {
117 log(
"File exists as filename only: '%s'", justFilename.c_str());
121 log(LogLevel::WARNING,
"File not found: '%s'", filePath.c_str());
125 char getOsSeparator()
const override {
129 Assimp::IOStream* Open(
const char* pFile,
const char* pMode)
override {
131 if (std::strstr(pMode,
"r") ==
nullptr) {
135 std::string filePath(pFile);
136 log(
"Assimp trying to open: '%s'", filePath.c_str());
138 std::string finalPath;
143 if (m_vfs->file_exists(filePath)) {
144 finalPath = filePath;
145 log(
"Opening file as-is: '%s'", finalPath.c_str());
148 else if (m_vfs->file_exists(m_baseDir + filePath)) {
149 finalPath = m_baseDir + filePath;
150 log(
"Opening file relative to base: '%s'", finalPath.c_str());
154 std::filesystem::path pathObj(filePath);
155 std::string justFilename = pathObj.filename().string();
156 if (m_vfs->file_exists(justFilename)) {
157 finalPath = justFilename;
158 log(
"Opening file as filename only: '%s'", finalPath.c_str());
160 log(LogLevel::ERROR,
"Failed to find file: '%s'", filePath.c_str());
166 auto fileData = m_vfs->load_file(finalPath);
168 log(
"Failed to load file data: '%s'", finalPath.c_str());
172 log(
"Successfully loaded file: '%s' (%zu bytes)", finalPath.c_str(), fileData->size);
175 return new VPKAssimpStream(
reinterpret_cast<const char*
>(fileData->data.data()), fileData->size);
178 void Close(Assimp::IOStream* pFile)
override {
184 if (scene->mNumMeshes == 0) {
189 submeshes.resize(scene->mNumMeshes);
191 for (
unsigned m = 0; m < scene->mNumMeshes; m++) {
192 log(
"Processing mesh %i...", m);
193 aiMesh* aiMesh = scene->mMeshes[m];
194 Submesh& submesh = submeshes[m];
196 submesh.vertices.resize(aiMesh->mNumVertices);
197 for (
unsigned i = 0; i < aiMesh->mNumVertices; i++) {
198 submesh.vertices[i].position = {
199 aiMesh->mVertices[i].x,
200 aiMesh->mVertices[i].y,
201 aiMesh->mVertices[i].z
204 if(aiMesh->mNormals) {
205 submesh.vertices[i].normal = {
206 aiMesh->mNormals[i].x,
207 aiMesh->mNormals[i].y,
208 aiMesh->mNormals[i].z
212 if (aiMesh->mTextureCoords[0]) {
213 submesh.vertices[i].uv = {
214 aiMesh->mTextureCoords[0][i].x,
215 aiMesh->mTextureCoords[0][i].y
218 submesh.vertices[i].uv = glm::vec2(-100000.f);
222 submesh.indices.reserve(aiMesh->mNumFaces * 3);
223 for (
unsigned i = 0; i < aiMesh->mNumFaces; i++) {
224 aiFace face = aiMesh->mFaces[i];
225 for (
unsigned j = 0; j < face.mNumIndices; j++) {
226 submesh.indices.push_back(face.mIndices[j]);
230 if (aiMesh->mMaterialIndex >= 0) {
231 aiMaterial* material = scene->mMaterials[aiMesh->mMaterialIndex];
233 if (material->GetTexture(aiTextureType_DIFFUSE, 0, &texPath) == AI_SUCCESS) {
234 submesh.texturePath = textureBaseDir + texPath.C_Str();
242 std::filesystem::path fullPath = execDir / relativePath;
243 std::string pathStr = fullPath.string();
245 log(
"Loading raw mesh from: %s", pathStr.c_str());
247 if (!std::filesystem::exists(fullPath)) {
248 handle_exception(std::runtime_error(
"Raw file does not exist: " + pathStr));
252 Assimp::Importer importer;
253 const aiScene* scene = importer.ReadFile(pathStr,
254 aiProcess_Triangulate |
255 aiProcess_GenNormals |
258 if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
259 handle_exception(std::runtime_error(
"Assimp failed to load raw file: " + std::string(importer.GetErrorString())));
263 std::filesystem::path meshFolder = fullPath;
264 meshFolder.remove_filename();
272 log(
"Using Assimp version: %d.%d.%d", aiGetVersionMajor(), aiGetVersionMinor(), aiGetVersionRevision());
273 log(
"Creating assimp importer...");
275 auto importerPtr = std::make_unique<Assimp::Importer>();
276 Assimp::Importer& importer = *importerPtr;
278 std::string realPath = path;
287 handle_exception(std::runtime_error(
"File: [" + realPath +
"] doesnt exist"));
291 const aiScene* scene =
nullptr;
294 auto fileData = vfs->
load_file(realPath);
295 if (!fileData)
throw_error(
"Failed to load file from VFS: " + realPath);
297 log(
"Loading from VFS memory buffer, size: %zu", fileData->size);
299 std::string extension = std::filesystem::path(realPath).extension().string();
300 scene = importer.ReadFileFromMemory(
301 fileData->data.data(),
303 aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_FlipUVs,
306 scene = importer.ReadFile(realPath,
307 aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_FlipUVs);
310 if (!scene || (scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE) || !scene->mRootNode) {
311 handle_exception(std::runtime_error(
"Assimp failed to load file: " + std::string(importer.GetErrorString())));
315 std::filesystem::path meshFolderPath(realPath);
316 meshFolderPath.remove_filename();
320 if (vfs) importer.SetIOHandler(
nullptr);