3DSLoader.cpp 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347
  1. /*
  2. ---------------------------------------------------------------------------
  3. Open Asset Import Library (assimp)
  4. ---------------------------------------------------------------------------
  5. Copyright (c) 2006-2021, assimp team
  6. All rights reserved.
  7. Redistribution and use of this software in source and binary forms,
  8. with or without modification, are permitted provided that the following
  9. conditions are met:
  10. * Redistributions of source code must retain the above
  11. copyright notice, this list of conditions and the
  12. following disclaimer.
  13. * Redistributions in binary form must reproduce the above
  14. copyright notice, this list of conditions and the
  15. following disclaimer in the documentation and/or other
  16. materials provided with the distribution.
  17. * Neither the name of the assimp team, nor the names of its
  18. contributors may be used to endorse or promote products
  19. derived from this software without specific prior
  20. written permission of the assimp team.
  21. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  24. A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  25. OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  26. SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  27. LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  28. DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  29. THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  30. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  31. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32. ---------------------------------------------------------------------------
  33. */
  34. /** @file 3DSLoader.cpp
  35. * @brief Implementation of the 3ds importer class
  36. *
  37. * http://www.the-labs.com/Blender/3DS-details.html
  38. */
  39. #ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
  40. #include "3DSLoader.h"
  41. #include <assimp/StringComparison.h>
  42. #include <assimp/importerdesc.h>
  43. #include <assimp/scene.h>
  44. #include <assimp/DefaultLogger.hpp>
  45. #include <assimp/IOSystem.hpp>
  46. using namespace Assimp;
  47. static const aiImporterDesc desc = {
  48. "Discreet 3DS Importer",
  49. "",
  50. "",
  51. "Limited animation support",
  52. aiImporterFlags_SupportBinaryFlavour,
  53. 0,
  54. 0,
  55. 0,
  56. 0,
  57. "3ds prj"
  58. };
  59. // ------------------------------------------------------------------------------------------------
  60. // Begins a new parsing block
  61. // - Reads the current chunk and validates it
  62. // - computes its length
  63. #define ASSIMP_3DS_BEGIN_CHUNK() \
  64. while (true) { \
  65. if (stream->GetRemainingSizeToLimit() < sizeof(Discreet3DS::Chunk)) { \
  66. return; \
  67. } \
  68. Discreet3DS::Chunk chunk; \
  69. ReadChunk(&chunk); \
  70. int chunkSize = chunk.Size - sizeof(Discreet3DS::Chunk); \
  71. if (chunkSize <= 0) \
  72. continue; \
  73. const unsigned int oldReadLimit = stream->SetReadLimit( \
  74. stream->GetCurrentPos() + chunkSize);
  75. // ------------------------------------------------------------------------------------------------
  76. // End a parsing block
  77. // Must follow at the end of each parsing block, reset chunk end marker to previous value
  78. #define ASSIMP_3DS_END_CHUNK() \
  79. stream->SkipToReadLimit(); \
  80. stream->SetReadLimit(oldReadLimit); \
  81. if (stream->GetRemainingSizeToLimit() == 0) \
  82. return; \
  83. }
  84. // ------------------------------------------------------------------------------------------------
  85. // Constructor to be privately used by Importer
  86. Discreet3DSImporter::Discreet3DSImporter() :
  87. stream(), mLastNodeIndex(), mCurrentNode(), mRootNode(), mScene(), mMasterScale(), bHasBG(), bIsPrj() {
  88. // empty
  89. }
  90. // ------------------------------------------------------------------------------------------------
  91. // Destructor, private as well
  92. Discreet3DSImporter::~Discreet3DSImporter() {
  93. // empty
  94. }
  95. // ------------------------------------------------------------------------------------------------
  96. // Returns whether the class can handle the format of the given file.
  97. bool Discreet3DSImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const {
  98. std::string extension = GetExtension(pFile);
  99. if (extension == "3ds" || extension == "prj") {
  100. return true;
  101. }
  102. if (!extension.length() || checkSig) {
  103. uint16_t token[3];
  104. token[0] = 0x4d4d;
  105. token[1] = 0x3dc2;
  106. //token[2] = 0x3daa;
  107. return CheckMagicToken(pIOHandler, pFile, token, 2, 0, 2);
  108. }
  109. return false;
  110. }
  111. // ------------------------------------------------------------------------------------------------
  112. // Loader registry entry
  113. const aiImporterDesc *Discreet3DSImporter::GetInfo() const {
  114. return &desc;
  115. }
  116. // ------------------------------------------------------------------------------------------------
  117. // Setup configuration properties
  118. void Discreet3DSImporter::SetupProperties(const Importer * /*pImp*/) {
  119. // nothing to be done for the moment
  120. }
  121. // ------------------------------------------------------------------------------------------------
  122. // Imports the given file into the given scene structure.
  123. void Discreet3DSImporter::InternReadFile(const std::string &pFile,
  124. aiScene *pScene, IOSystem *pIOHandler) {
  125. auto theFile = pIOHandler->Open(pFile, "rb");
  126. if (!theFile) {
  127. throw DeadlyImportError("3DS: Could not open ", pFile);
  128. }
  129. StreamReaderLE theStream(theFile);
  130. // We should have at least one chunk
  131. if (theStream.GetRemainingSize() < 16) {
  132. throw DeadlyImportError("3DS file is either empty or corrupt: ", pFile);
  133. }
  134. this->stream = &theStream;
  135. // Allocate our temporary 3DS representation
  136. D3DS::Scene _scene;
  137. mScene = &_scene;
  138. // Initialize members
  139. D3DS::Node _rootNode("UNNAMED");
  140. mLastNodeIndex = -1;
  141. mCurrentNode = &_rootNode;
  142. mRootNode = mCurrentNode;
  143. mRootNode->mHierarchyPos = -1;
  144. mRootNode->mHierarchyIndex = -1;
  145. mRootNode->mParent = nullptr;
  146. mMasterScale = 1.0f;
  147. mBackgroundImage = std::string();
  148. bHasBG = false;
  149. bIsPrj = false;
  150. // Parse the file
  151. ParseMainChunk();
  152. // Process all meshes in the file. First check whether all
  153. // face indices have valid values. The generate our
  154. // internal verbose representation. Finally compute normal
  155. // vectors from the smoothing groups we read from the
  156. // file.
  157. for (auto &mesh : mScene->mMeshes) {
  158. if (mesh.mFaces.size() > 0 && mesh.mPositions.size() == 0) {
  159. throw DeadlyImportError("3DS file contains faces but no vertices: ", pFile);
  160. }
  161. CheckIndices(mesh);
  162. MakeUnique(mesh);
  163. ComputeNormalsWithSmoothingsGroups<D3DS::Face>(mesh);
  164. }
  165. // Replace all occurrences of the default material with a
  166. // valid material. Generate it if no material containing
  167. // DEFAULT in its name has been found in the file
  168. ReplaceDefaultMaterial();
  169. // Convert the scene from our internal representation to an
  170. // aiScene object. This involves copying all meshes, lights
  171. // and cameras to the scene
  172. ConvertScene(pScene);
  173. // Generate the node graph for the scene. This is a little bit
  174. // tricky since we'll need to split some meshes into sub-meshes
  175. GenerateNodeGraph(pScene);
  176. // Now apply the master scaling factor to the scene
  177. ApplyMasterScale(pScene);
  178. // Our internal scene representation and the root
  179. // node will be automatically deleted, so the whole hierarchy will follow
  180. AI_DEBUG_INVALIDATE_PTR(mRootNode);
  181. AI_DEBUG_INVALIDATE_PTR(mScene);
  182. AI_DEBUG_INVALIDATE_PTR(this->stream);
  183. }
  184. // ------------------------------------------------------------------------------------------------
  185. // Applies a master-scaling factor to the imported scene
  186. void Discreet3DSImporter::ApplyMasterScale(aiScene *pScene) {
  187. // There are some 3DS files with a zero scaling factor
  188. if (!mMasterScale)
  189. mMasterScale = 1.0f;
  190. else
  191. mMasterScale = 1.0f / mMasterScale;
  192. // Construct an uniform scaling matrix and multiply with it
  193. pScene->mRootNode->mTransformation *= aiMatrix4x4(
  194. mMasterScale, 0.0f, 0.0f, 0.0f,
  195. 0.0f, mMasterScale, 0.0f, 0.0f,
  196. 0.0f, 0.0f, mMasterScale, 0.0f,
  197. 0.0f, 0.0f, 0.0f, 1.0f);
  198. // Check whether a scaling track is assigned to the root node.
  199. }
  200. // ------------------------------------------------------------------------------------------------
  201. // Reads a new chunk from the file
  202. void Discreet3DSImporter::ReadChunk(Discreet3DS::Chunk *pcOut) {
  203. ai_assert(pcOut != nullptr);
  204. pcOut->Flag = stream->GetI2();
  205. pcOut->Size = stream->GetI4();
  206. if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSize()) {
  207. throw DeadlyImportError("Chunk is too large");
  208. }
  209. if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSizeToLimit()) {
  210. ASSIMP_LOG_ERROR("3DS: Chunk overflow");
  211. }
  212. }
  213. // ------------------------------------------------------------------------------------------------
  214. // Skip a chunk
  215. void Discreet3DSImporter::SkipChunk() {
  216. Discreet3DS::Chunk psChunk;
  217. ReadChunk(&psChunk);
  218. stream->IncPtr(psChunk.Size - sizeof(Discreet3DS::Chunk));
  219. return;
  220. }
  221. // ------------------------------------------------------------------------------------------------
  222. // Process the primary chunk of the file
  223. void Discreet3DSImporter::ParseMainChunk() {
  224. ASSIMP_3DS_BEGIN_CHUNK();
  225. // get chunk type
  226. switch (chunk.Flag) {
  227. case Discreet3DS::CHUNK_PRJ:
  228. bIsPrj = true;
  229. break;
  230. case Discreet3DS::CHUNK_MAIN:
  231. ParseEditorChunk();
  232. break;
  233. };
  234. ASSIMP_3DS_END_CHUNK();
  235. // recursively continue processing this hierarchy level
  236. return ParseMainChunk();
  237. }
  238. // ------------------------------------------------------------------------------------------------
  239. void Discreet3DSImporter::ParseEditorChunk() {
  240. ASSIMP_3DS_BEGIN_CHUNK();
  241. // get chunk type
  242. switch (chunk.Flag) {
  243. case Discreet3DS::CHUNK_OBJMESH:
  244. ParseObjectChunk();
  245. break;
  246. // NOTE: In several documentations in the internet this
  247. // chunk appears at different locations
  248. case Discreet3DS::CHUNK_KEYFRAMER:
  249. ParseKeyframeChunk();
  250. break;
  251. case Discreet3DS::CHUNK_VERSION: {
  252. // print the version number
  253. char buff[10];
  254. ASSIMP_itoa10(buff, stream->GetI2());
  255. ASSIMP_LOG_INFO("3DS file format version: ", buff);
  256. } break;
  257. };
  258. ASSIMP_3DS_END_CHUNK();
  259. }
  260. // ------------------------------------------------------------------------------------------------
  261. void Discreet3DSImporter::ParseObjectChunk() {
  262. ASSIMP_3DS_BEGIN_CHUNK();
  263. // get chunk type
  264. switch (chunk.Flag) {
  265. case Discreet3DS::CHUNK_OBJBLOCK: {
  266. unsigned int cnt = 0;
  267. const char *sz = (const char *)stream->GetPtr();
  268. // Get the name of the geometry object
  269. while (stream->GetI1())
  270. ++cnt;
  271. ParseChunk(sz, cnt);
  272. } break;
  273. case Discreet3DS::CHUNK_MAT_MATERIAL:
  274. // Add a new material to the list
  275. mScene->mMaterials.push_back(D3DS::Material(std::string("UNNAMED_" + ai_to_string(mScene->mMaterials.size()))));
  276. ParseMaterialChunk();
  277. break;
  278. case Discreet3DS::CHUNK_AMBCOLOR:
  279. // This is the ambient base color of the scene.
  280. // We add it to the ambient color of all materials
  281. ParseColorChunk(&mClrAmbient, true);
  282. if (is_qnan(mClrAmbient.r)) {
  283. // We failed to read the ambient base color.
  284. ASSIMP_LOG_ERROR("3DS: Failed to read ambient base color");
  285. mClrAmbient.r = mClrAmbient.g = mClrAmbient.b = 0.0f;
  286. }
  287. break;
  288. case Discreet3DS::CHUNK_BIT_MAP: {
  289. // Specifies the background image. The string should already be
  290. // properly 0 terminated but we need to be sure
  291. unsigned int cnt = 0;
  292. const char *sz = (const char *)stream->GetPtr();
  293. while (stream->GetI1())
  294. ++cnt;
  295. mBackgroundImage = std::string(sz, cnt);
  296. } break;
  297. case Discreet3DS::CHUNK_BIT_MAP_EXISTS:
  298. bHasBG = true;
  299. break;
  300. case Discreet3DS::CHUNK_MASTER_SCALE:
  301. // Scene master scaling factor
  302. mMasterScale = stream->GetF4();
  303. break;
  304. };
  305. ASSIMP_3DS_END_CHUNK();
  306. }
  307. // ------------------------------------------------------------------------------------------------
  308. void Discreet3DSImporter::ParseChunk(const char *name, unsigned int num) {
  309. ASSIMP_3DS_BEGIN_CHUNK();
  310. // IMPLEMENTATION NOTE;
  311. // Cameras or lights define their transformation in their parent node and in the
  312. // corresponding light or camera chunks. However, we read and process the latter
  313. // to to be able to return valid cameras/lights even if no scenegraph is given.
  314. // get chunk type
  315. switch (chunk.Flag) {
  316. case Discreet3DS::CHUNK_TRIMESH: {
  317. // this starts a new triangle mesh
  318. mScene->mMeshes.push_back(D3DS::Mesh(std::string(name, num)));
  319. // Read mesh chunks
  320. ParseMeshChunk();
  321. } break;
  322. case Discreet3DS::CHUNK_LIGHT: {
  323. // This starts a new light
  324. aiLight *light = new aiLight();
  325. mScene->mLights.push_back(light);
  326. light->mName.Set(std::string(name, num));
  327. // First read the position of the light
  328. light->mPosition.x = stream->GetF4();
  329. light->mPosition.y = stream->GetF4();
  330. light->mPosition.z = stream->GetF4();
  331. light->mColorDiffuse = aiColor3D(1.f, 1.f, 1.f);
  332. // Now check for further subchunks
  333. if (!bIsPrj) /* fixme */
  334. ParseLightChunk();
  335. // The specular light color is identical the the diffuse light color. The ambient light color
  336. // is equal to the ambient base color of the whole scene.
  337. light->mColorSpecular = light->mColorDiffuse;
  338. light->mColorAmbient = mClrAmbient;
  339. if (light->mType == aiLightSource_UNDEFINED) {
  340. // It must be a point light
  341. light->mType = aiLightSource_POINT;
  342. }
  343. } break;
  344. case Discreet3DS::CHUNK_CAMERA: {
  345. // This starts a new camera
  346. aiCamera *camera = new aiCamera();
  347. mScene->mCameras.push_back(camera);
  348. camera->mName.Set(std::string(name, num));
  349. // First read the position of the camera
  350. camera->mPosition.x = stream->GetF4();
  351. camera->mPosition.y = stream->GetF4();
  352. camera->mPosition.z = stream->GetF4();
  353. // Then the camera target
  354. camera->mLookAt.x = stream->GetF4() - camera->mPosition.x;
  355. camera->mLookAt.y = stream->GetF4() - camera->mPosition.y;
  356. camera->mLookAt.z = stream->GetF4() - camera->mPosition.z;
  357. ai_real len = camera->mLookAt.Length();
  358. if (len < 1e-5) {
  359. // There are some files with lookat == position. Don't know why or whether it's ok or not.
  360. ASSIMP_LOG_ERROR("3DS: Unable to read proper camera look-at vector");
  361. camera->mLookAt = aiVector3D(0.0, 1.0, 0.0);
  362. } else
  363. camera->mLookAt /= len;
  364. // And finally - the camera rotation angle, in counter clockwise direction
  365. const ai_real angle = AI_DEG_TO_RAD(stream->GetF4());
  366. aiQuaternion quat(camera->mLookAt, angle);
  367. camera->mUp = quat.GetMatrix() * aiVector3D(0.0, 1.0, 0.0);
  368. // Read the lense angle
  369. camera->mHorizontalFOV = AI_DEG_TO_RAD(stream->GetF4());
  370. if (camera->mHorizontalFOV < 0.001f) {
  371. camera->mHorizontalFOV = AI_DEG_TO_RAD(45.f);
  372. }
  373. // Now check for further subchunks
  374. if (!bIsPrj) /* fixme */ {
  375. ParseCameraChunk();
  376. }
  377. } break;
  378. };
  379. ASSIMP_3DS_END_CHUNK();
  380. }
  381. // ------------------------------------------------------------------------------------------------
  382. void Discreet3DSImporter::ParseLightChunk() {
  383. ASSIMP_3DS_BEGIN_CHUNK();
  384. aiLight *light = mScene->mLights.back();
  385. // get chunk type
  386. switch (chunk.Flag) {
  387. case Discreet3DS::CHUNK_DL_SPOTLIGHT:
  388. // Now we can be sure that the light is a spot light
  389. light->mType = aiLightSource_SPOT;
  390. // We wouldn't need to normalize here, but we do it
  391. light->mDirection.x = stream->GetF4() - light->mPosition.x;
  392. light->mDirection.y = stream->GetF4() - light->mPosition.y;
  393. light->mDirection.z = stream->GetF4() - light->mPosition.z;
  394. light->mDirection.Normalize();
  395. // Now the hotspot and falloff angles - in degrees
  396. light->mAngleInnerCone = AI_DEG_TO_RAD(stream->GetF4());
  397. // FIX: the falloff angle is just an offset
  398. light->mAngleOuterCone = light->mAngleInnerCone + AI_DEG_TO_RAD(stream->GetF4());
  399. break;
  400. // intensity multiplier
  401. case Discreet3DS::CHUNK_DL_MULTIPLIER:
  402. light->mColorDiffuse = light->mColorDiffuse * stream->GetF4();
  403. break;
  404. // light color
  405. case Discreet3DS::CHUNK_RGBF:
  406. case Discreet3DS::CHUNK_LINRGBF:
  407. light->mColorDiffuse.r *= stream->GetF4();
  408. light->mColorDiffuse.g *= stream->GetF4();
  409. light->mColorDiffuse.b *= stream->GetF4();
  410. break;
  411. // light attenuation
  412. case Discreet3DS::CHUNK_DL_ATTENUATE:
  413. light->mAttenuationLinear = stream->GetF4();
  414. break;
  415. };
  416. ASSIMP_3DS_END_CHUNK();
  417. }
  418. // ------------------------------------------------------------------------------------------------
  419. void Discreet3DSImporter::ParseCameraChunk() {
  420. ASSIMP_3DS_BEGIN_CHUNK();
  421. aiCamera *camera = mScene->mCameras.back();
  422. // get chunk type
  423. switch (chunk.Flag) {
  424. // near and far clip plane
  425. case Discreet3DS::CHUNK_CAM_RANGES:
  426. camera->mClipPlaneNear = stream->GetF4();
  427. camera->mClipPlaneFar = stream->GetF4();
  428. break;
  429. }
  430. ASSIMP_3DS_END_CHUNK();
  431. }
  432. // ------------------------------------------------------------------------------------------------
  433. void Discreet3DSImporter::ParseKeyframeChunk() {
  434. ASSIMP_3DS_BEGIN_CHUNK();
  435. // get chunk type
  436. switch (chunk.Flag) {
  437. case Discreet3DS::CHUNK_TRACKCAMTGT:
  438. case Discreet3DS::CHUNK_TRACKSPOTL:
  439. case Discreet3DS::CHUNK_TRACKCAMERA:
  440. case Discreet3DS::CHUNK_TRACKINFO:
  441. case Discreet3DS::CHUNK_TRACKLIGHT:
  442. case Discreet3DS::CHUNK_TRACKLIGTGT:
  443. // this starts a new mesh hierarchy chunk
  444. ParseHierarchyChunk(chunk.Flag);
  445. break;
  446. };
  447. ASSIMP_3DS_END_CHUNK();
  448. }
  449. // ------------------------------------------------------------------------------------------------
  450. // Little helper function for ParseHierarchyChunk
  451. void Discreet3DSImporter::InverseNodeSearch(D3DS::Node *pcNode, D3DS::Node *pcCurrent) {
  452. if (!pcCurrent) {
  453. mRootNode->push_back(pcNode);
  454. return;
  455. }
  456. if (pcCurrent->mHierarchyPos == pcNode->mHierarchyPos) {
  457. if (pcCurrent->mParent) {
  458. pcCurrent->mParent->push_back(pcNode);
  459. } else
  460. pcCurrent->push_back(pcNode);
  461. return;
  462. }
  463. return InverseNodeSearch(pcNode, pcCurrent->mParent);
  464. }
  465. // ------------------------------------------------------------------------------------------------
  466. // Find a node with a specific name in the import hierarchy
  467. D3DS::Node *FindNode(D3DS::Node *root, const std::string &name) {
  468. if (root->mName == name) {
  469. return root;
  470. }
  471. for (std::vector<D3DS::Node *>::iterator it = root->mChildren.begin(); it != root->mChildren.end(); ++it) {
  472. D3DS::Node *nd = FindNode(*it, name);
  473. if (nullptr != nd) {
  474. return nd;
  475. }
  476. }
  477. return nullptr;
  478. }
  479. // ------------------------------------------------------------------------------------------------
  480. // Binary predicate for std::unique()
  481. template <class T>
  482. bool KeyUniqueCompare(const T &first, const T &second) {
  483. return first.mTime == second.mTime;
  484. }
  485. // ------------------------------------------------------------------------------------------------
  486. // Skip some additional import data.
  487. void Discreet3DSImporter::SkipTCBInfo() {
  488. unsigned int flags = stream->GetI2();
  489. if (!flags) {
  490. // Currently we can't do anything with these values. They occur
  491. // quite rare, so it wouldn't be worth the effort implementing
  492. // them. 3DS is not really suitable for complex animations,
  493. // so full support is not required.
  494. ASSIMP_LOG_WARN("3DS: Skipping TCB animation info");
  495. }
  496. if (flags & Discreet3DS::KEY_USE_TENS) {
  497. stream->IncPtr(4);
  498. }
  499. if (flags & Discreet3DS::KEY_USE_BIAS) {
  500. stream->IncPtr(4);
  501. }
  502. if (flags & Discreet3DS::KEY_USE_CONT) {
  503. stream->IncPtr(4);
  504. }
  505. if (flags & Discreet3DS::KEY_USE_EASE_FROM) {
  506. stream->IncPtr(4);
  507. }
  508. if (flags & Discreet3DS::KEY_USE_EASE_TO) {
  509. stream->IncPtr(4);
  510. }
  511. }
  512. // ------------------------------------------------------------------------------------------------
  513. // Read hierarchy and keyframe info
  514. void Discreet3DSImporter::ParseHierarchyChunk(uint16_t parent) {
  515. ASSIMP_3DS_BEGIN_CHUNK();
  516. // get chunk type
  517. switch (chunk.Flag) {
  518. case Discreet3DS::CHUNK_TRACKOBJNAME:
  519. // This is the name of the object to which the track applies. The chunk also
  520. // defines the position of this object in the hierarchy.
  521. {
  522. // First of all: get the name of the object
  523. unsigned int cnt = 0;
  524. const char *sz = (const char *)stream->GetPtr();
  525. while (stream->GetI1())
  526. ++cnt;
  527. std::string name = std::string(sz, cnt);
  528. // Now find out whether we have this node already (target animation channels
  529. // are stored with a separate object ID)
  530. D3DS::Node *pcNode = FindNode(mRootNode, name);
  531. int instanceNumber = 1;
  532. if (pcNode) {
  533. // if the source is not a CHUNK_TRACKINFO block it won't be an object instance
  534. if (parent != Discreet3DS::CHUNK_TRACKINFO) {
  535. mCurrentNode = pcNode;
  536. break;
  537. }
  538. pcNode->mInstanceCount++;
  539. instanceNumber = pcNode->mInstanceCount;
  540. }
  541. pcNode = new D3DS::Node(name);
  542. pcNode->mInstanceNumber = instanceNumber;
  543. // There are two unknown values which we can safely ignore
  544. stream->IncPtr(4);
  545. // Now read the hierarchy position of the object
  546. uint16_t hierarchy = stream->GetI2() + 1;
  547. pcNode->mHierarchyPos = hierarchy;
  548. pcNode->mHierarchyIndex = mLastNodeIndex;
  549. // And find a proper position in the graph for it
  550. if (mCurrentNode && mCurrentNode->mHierarchyPos == hierarchy) {
  551. // add to the parent of the last touched node
  552. mCurrentNode->mParent->push_back(pcNode);
  553. mLastNodeIndex++;
  554. } else if (hierarchy >= mLastNodeIndex) {
  555. // place it at the current position in the hierarchy
  556. mCurrentNode->push_back(pcNode);
  557. mLastNodeIndex = hierarchy;
  558. } else {
  559. // need to go back to the specified position in the hierarchy.
  560. InverseNodeSearch(pcNode, mCurrentNode);
  561. mLastNodeIndex++;
  562. }
  563. // Make this node the current node
  564. mCurrentNode = pcNode;
  565. }
  566. break;
  567. case Discreet3DS::CHUNK_TRACKDUMMYOBJNAME:
  568. // This is the "real" name of a $$$DUMMY object
  569. {
  570. const char *sz = (const char *)stream->GetPtr();
  571. while (stream->GetI1())
  572. ;
  573. // If object name is DUMMY, take this one instead
  574. if (mCurrentNode->mName == "$$$DUMMY") {
  575. mCurrentNode->mName = std::string(sz);
  576. break;
  577. }
  578. }
  579. break;
  580. case Discreet3DS::CHUNK_TRACKPIVOT:
  581. if (Discreet3DS::CHUNK_TRACKINFO != parent) {
  582. ASSIMP_LOG_WARN("3DS: Skipping pivot subchunk for non usual object");
  583. break;
  584. }
  585. // Pivot = origin of rotation and scaling
  586. mCurrentNode->vPivot.x = stream->GetF4();
  587. mCurrentNode->vPivot.y = stream->GetF4();
  588. mCurrentNode->vPivot.z = stream->GetF4();
  589. break;
  590. // ////////////////////////////////////////////////////////////////////
  591. // POSITION KEYFRAME
  592. case Discreet3DS::CHUNK_TRACKPOS: {
  593. stream->IncPtr(10);
  594. const unsigned int numFrames = stream->GetI4();
  595. bool sortKeys = false;
  596. // This could also be meant as the target position for
  597. // (targeted) lights and cameras
  598. std::vector<aiVectorKey> *l;
  599. if (Discreet3DS::CHUNK_TRACKCAMTGT == parent || Discreet3DS::CHUNK_TRACKLIGTGT == parent) {
  600. l = &mCurrentNode->aTargetPositionKeys;
  601. } else
  602. l = &mCurrentNode->aPositionKeys;
  603. l->reserve(numFrames);
  604. for (unsigned int i = 0; i < numFrames; ++i) {
  605. const unsigned int fidx = stream->GetI4();
  606. // Setup a new position key
  607. aiVectorKey v;
  608. v.mTime = (double)fidx;
  609. SkipTCBInfo();
  610. v.mValue.x = stream->GetF4();
  611. v.mValue.y = stream->GetF4();
  612. v.mValue.z = stream->GetF4();
  613. // check whether we'll need to sort the keys
  614. if (!l->empty() && v.mTime <= l->back().mTime)
  615. sortKeys = true;
  616. // Add the new keyframe to the list
  617. l->push_back(v);
  618. }
  619. // Sort all keys with ascending time values and remove duplicates?
  620. if (sortKeys) {
  621. std::stable_sort(l->begin(), l->end());
  622. l->erase(std::unique(l->begin(), l->end(), &KeyUniqueCompare<aiVectorKey>), l->end());
  623. }
  624. }
  625. break;
  626. // ////////////////////////////////////////////////////////////////////
  627. // CAMERA ROLL KEYFRAME
  628. case Discreet3DS::CHUNK_TRACKROLL: {
  629. // roll keys are accepted for cameras only
  630. if (parent != Discreet3DS::CHUNK_TRACKCAMERA) {
  631. ASSIMP_LOG_WARN("3DS: Ignoring roll track for non-camera object");
  632. break;
  633. }
  634. bool sortKeys = false;
  635. std::vector<aiFloatKey> *l = &mCurrentNode->aCameraRollKeys;
  636. stream->IncPtr(10);
  637. const unsigned int numFrames = stream->GetI4();
  638. l->reserve(numFrames);
  639. for (unsigned int i = 0; i < numFrames; ++i) {
  640. const unsigned int fidx = stream->GetI4();
  641. // Setup a new position key
  642. aiFloatKey v;
  643. v.mTime = (double)fidx;
  644. // This is just a single float
  645. SkipTCBInfo();
  646. v.mValue = stream->GetF4();
  647. // Check whether we'll need to sort the keys
  648. if (!l->empty() && v.mTime <= l->back().mTime)
  649. sortKeys = true;
  650. // Add the new keyframe to the list
  651. l->push_back(v);
  652. }
  653. // Sort all keys with ascending time values and remove duplicates?
  654. if (sortKeys) {
  655. std::stable_sort(l->begin(), l->end());
  656. l->erase(std::unique(l->begin(), l->end(), &KeyUniqueCompare<aiFloatKey>), l->end());
  657. }
  658. } break;
  659. // ////////////////////////////////////////////////////////////////////
  660. // CAMERA FOV KEYFRAME
  661. case Discreet3DS::CHUNK_TRACKFOV: {
  662. ASSIMP_LOG_ERROR("3DS: Skipping FOV animation track. "
  663. "This is not supported");
  664. } break;
  665. // ////////////////////////////////////////////////////////////////////
  666. // ROTATION KEYFRAME
  667. case Discreet3DS::CHUNK_TRACKROTATE: {
  668. stream->IncPtr(10);
  669. const unsigned int numFrames = stream->GetI4();
  670. bool sortKeys = false;
  671. std::vector<aiQuatKey> *l = &mCurrentNode->aRotationKeys;
  672. l->reserve(numFrames);
  673. for (unsigned int i = 0; i < numFrames; ++i) {
  674. const unsigned int fidx = stream->GetI4();
  675. SkipTCBInfo();
  676. aiQuatKey v;
  677. v.mTime = (double)fidx;
  678. // The rotation keyframe is given as an axis-angle pair
  679. const float rad = stream->GetF4();
  680. aiVector3D axis;
  681. axis.x = stream->GetF4();
  682. axis.y = stream->GetF4();
  683. axis.z = stream->GetF4();
  684. if (!axis.x && !axis.y && !axis.z)
  685. axis.y = 1.f;
  686. // Construct a rotation quaternion from the axis-angle pair
  687. v.mValue = aiQuaternion(axis, rad);
  688. // Check whether we'll need to sort the keys
  689. if (!l->empty() && v.mTime <= l->back().mTime)
  690. sortKeys = true;
  691. // add the new keyframe to the list
  692. l->push_back(v);
  693. }
  694. // Sort all keys with ascending time values and remove duplicates?
  695. if (sortKeys) {
  696. std::stable_sort(l->begin(), l->end());
  697. l->erase(std::unique(l->begin(), l->end(), &KeyUniqueCompare<aiQuatKey>), l->end());
  698. }
  699. } break;
  700. // ////////////////////////////////////////////////////////////////////
  701. // SCALING KEYFRAME
  702. case Discreet3DS::CHUNK_TRACKSCALE: {
  703. stream->IncPtr(10);
  704. const unsigned int numFrames = stream->GetI2();
  705. stream->IncPtr(2);
  706. bool sortKeys = false;
  707. std::vector<aiVectorKey> *l = &mCurrentNode->aScalingKeys;
  708. l->reserve(numFrames);
  709. for (unsigned int i = 0; i < numFrames; ++i) {
  710. const unsigned int fidx = stream->GetI4();
  711. SkipTCBInfo();
  712. // Setup a new key
  713. aiVectorKey v;
  714. v.mTime = (double)fidx;
  715. // ... and read its value
  716. v.mValue.x = stream->GetF4();
  717. v.mValue.y = stream->GetF4();
  718. v.mValue.z = stream->GetF4();
  719. // check whether we'll need to sort the keys
  720. if (!l->empty() && v.mTime <= l->back().mTime)
  721. sortKeys = true;
  722. // Remove zero-scalings on singular axes - they've been reported to be there erroneously in some strange files
  723. if (!v.mValue.x) v.mValue.x = 1.f;
  724. if (!v.mValue.y) v.mValue.y = 1.f;
  725. if (!v.mValue.z) v.mValue.z = 1.f;
  726. l->push_back(v);
  727. }
  728. // Sort all keys with ascending time values and remove duplicates?
  729. if (sortKeys) {
  730. std::stable_sort(l->begin(), l->end());
  731. l->erase(std::unique(l->begin(), l->end(), &KeyUniqueCompare<aiVectorKey>), l->end());
  732. }
  733. } break;
  734. };
  735. ASSIMP_3DS_END_CHUNK();
  736. }
  737. // ------------------------------------------------------------------------------------------------
  738. // Read a face chunk - it contains smoothing groups and material assignments
  739. void Discreet3DSImporter::ParseFaceChunk() {
  740. ASSIMP_3DS_BEGIN_CHUNK();
  741. // Get the mesh we're currently working on
  742. D3DS::Mesh &mMesh = mScene->mMeshes.back();
  743. // Get chunk type
  744. switch (chunk.Flag) {
  745. case Discreet3DS::CHUNK_SMOOLIST: {
  746. // This is the list of smoothing groups - a bitfield for every face.
  747. // Up to 32 smoothing groups assigned to a single face.
  748. unsigned int num = chunkSize / 4, m = 0;
  749. if (num > mMesh.mFaces.size()) {
  750. throw DeadlyImportError("3DS: More smoothing groups than faces");
  751. }
  752. for (std::vector<D3DS::Face>::iterator i = mMesh.mFaces.begin(); m != num; ++i, ++m) {
  753. // nth bit is set for nth smoothing group
  754. (*i).iSmoothGroup = stream->GetI4();
  755. }
  756. } break;
  757. case Discreet3DS::CHUNK_FACEMAT: {
  758. // at fist an asciiz with the material name
  759. const char *sz = (const char *)stream->GetPtr();
  760. while (stream->GetI1())
  761. ;
  762. // find the index of the material
  763. unsigned int idx = 0xcdcdcdcd, cnt = 0;
  764. for (std::vector<D3DS::Material>::const_iterator i = mScene->mMaterials.begin(); i != mScene->mMaterials.end(); ++i, ++cnt) {
  765. // use case independent comparisons. hopefully it will work.
  766. if ((*i).mName.length() && !ASSIMP_stricmp(sz, (*i).mName.c_str())) {
  767. idx = cnt;
  768. break;
  769. }
  770. }
  771. if (0xcdcdcdcd == idx) {
  772. ASSIMP_LOG_ERROR("3DS: Unknown material: ", sz);
  773. }
  774. // Now continue and read all material indices
  775. cnt = (uint16_t)stream->GetI2();
  776. for (unsigned int i = 0; i < cnt; ++i) {
  777. unsigned int fidx = (uint16_t)stream->GetI2();
  778. // check range
  779. if (fidx >= mMesh.mFaceMaterials.size()) {
  780. ASSIMP_LOG_ERROR("3DS: Invalid face index in face material list");
  781. } else
  782. mMesh.mFaceMaterials[fidx] = idx;
  783. }
  784. } break;
  785. };
  786. ASSIMP_3DS_END_CHUNK();
  787. }
  788. // ------------------------------------------------------------------------------------------------
  789. // Read a mesh chunk. Here's the actual mesh data
  790. void Discreet3DSImporter::ParseMeshChunk() {
  791. ASSIMP_3DS_BEGIN_CHUNK();
  792. // Get the mesh we're currently working on
  793. D3DS::Mesh &mMesh = mScene->mMeshes.back();
  794. // get chunk type
  795. switch (chunk.Flag) {
  796. case Discreet3DS::CHUNK_VERTLIST: {
  797. // This is the list of all vertices in the current mesh
  798. int num = (int)(uint16_t)stream->GetI2();
  799. mMesh.mPositions.reserve(num);
  800. while (num-- > 0) {
  801. aiVector3D v;
  802. v.x = stream->GetF4();
  803. v.y = stream->GetF4();
  804. v.z = stream->GetF4();
  805. mMesh.mPositions.push_back(v);
  806. }
  807. } break;
  808. case Discreet3DS::CHUNK_TRMATRIX: {
  809. // This is the RLEATIVE transformation matrix of the current mesh. Vertices are
  810. // pretransformed by this matrix wonder.
  811. mMesh.mMat.a1 = stream->GetF4();
  812. mMesh.mMat.b1 = stream->GetF4();
  813. mMesh.mMat.c1 = stream->GetF4();
  814. mMesh.mMat.a2 = stream->GetF4();
  815. mMesh.mMat.b2 = stream->GetF4();
  816. mMesh.mMat.c2 = stream->GetF4();
  817. mMesh.mMat.a3 = stream->GetF4();
  818. mMesh.mMat.b3 = stream->GetF4();
  819. mMesh.mMat.c3 = stream->GetF4();
  820. mMesh.mMat.a4 = stream->GetF4();
  821. mMesh.mMat.b4 = stream->GetF4();
  822. mMesh.mMat.c4 = stream->GetF4();
  823. } break;
  824. case Discreet3DS::CHUNK_MAPLIST: {
  825. // This is the list of all UV coords in the current mesh
  826. int num = (int)(uint16_t)stream->GetI2();
  827. mMesh.mTexCoords.reserve(num);
  828. while (num-- > 0) {
  829. aiVector3D v;
  830. v.x = stream->GetF4();
  831. v.y = stream->GetF4();
  832. mMesh.mTexCoords.push_back(v);
  833. }
  834. } break;
  835. case Discreet3DS::CHUNK_FACELIST: {
  836. // This is the list of all faces in the current mesh
  837. int num = (int)(uint16_t)stream->GetI2();
  838. mMesh.mFaces.reserve(num);
  839. while (num-- > 0) {
  840. // 3DS faces are ALWAYS triangles
  841. mMesh.mFaces.push_back(D3DS::Face());
  842. D3DS::Face &sFace = mMesh.mFaces.back();
  843. sFace.mIndices[0] = (uint16_t)stream->GetI2();
  844. sFace.mIndices[1] = (uint16_t)stream->GetI2();
  845. sFace.mIndices[2] = (uint16_t)stream->GetI2();
  846. stream->IncPtr(2); // skip edge visibility flag
  847. }
  848. // Resize the material array (0xcdcdcdcd marks the default material; so if a face is
  849. // not referenced by a material, $$DEFAULT will be assigned to it)
  850. mMesh.mFaceMaterials.resize(mMesh.mFaces.size(), 0xcdcdcdcd);
  851. // Larger 3DS files could have multiple FACE chunks here
  852. chunkSize = (int)stream->GetRemainingSizeToLimit();
  853. if (chunkSize > (int)sizeof(Discreet3DS::Chunk))
  854. ParseFaceChunk();
  855. } break;
  856. };
  857. ASSIMP_3DS_END_CHUNK();
  858. }
  859. // ------------------------------------------------------------------------------------------------
  860. // Read a 3DS material chunk
  861. void Discreet3DSImporter::ParseMaterialChunk() {
  862. ASSIMP_3DS_BEGIN_CHUNK();
  863. switch (chunk.Flag) {
  864. case Discreet3DS::CHUNK_MAT_MATNAME:
  865. {
  866. // The material name string is already zero-terminated, but we need to be sure ...
  867. const char *sz = (const char *)stream->GetPtr();
  868. unsigned int cnt = 0;
  869. while (stream->GetI1())
  870. ++cnt;
  871. if (!cnt) {
  872. // This may not be, we use the default name instead
  873. ASSIMP_LOG_ERROR("3DS: Empty material name");
  874. } else
  875. mScene->mMaterials.back().mName = std::string(sz, cnt);
  876. } break;
  877. case Discreet3DS::CHUNK_MAT_DIFFUSE: {
  878. // This is the diffuse material color
  879. aiColor3D *pc = &mScene->mMaterials.back().mDiffuse;
  880. ParseColorChunk(pc);
  881. if (is_qnan(pc->r)) {
  882. // color chunk is invalid. Simply ignore it
  883. ASSIMP_LOG_ERROR("3DS: Unable to read DIFFUSE chunk");
  884. pc->r = pc->g = pc->b = 1.0f;
  885. }
  886. } break;
  887. case Discreet3DS::CHUNK_MAT_SPECULAR: {
  888. // This is the specular material color
  889. aiColor3D *pc = &mScene->mMaterials.back().mSpecular;
  890. ParseColorChunk(pc);
  891. if (is_qnan(pc->r)) {
  892. // color chunk is invalid. Simply ignore it
  893. ASSIMP_LOG_ERROR("3DS: Unable to read SPECULAR chunk");
  894. pc->r = pc->g = pc->b = 1.0f;
  895. }
  896. } break;
  897. case Discreet3DS::CHUNK_MAT_AMBIENT: {
  898. // This is the ambient material color
  899. aiColor3D *pc = &mScene->mMaterials.back().mAmbient;
  900. ParseColorChunk(pc);
  901. if (is_qnan(pc->r)) {
  902. // color chunk is invalid. Simply ignore it
  903. ASSIMP_LOG_ERROR("3DS: Unable to read AMBIENT chunk");
  904. pc->r = pc->g = pc->b = 0.0f;
  905. }
  906. } break;
  907. case Discreet3DS::CHUNK_MAT_SELF_ILLUM: {
  908. // This is the emissive material color
  909. aiColor3D *pc = &mScene->mMaterials.back().mEmissive;
  910. ParseColorChunk(pc);
  911. if (is_qnan(pc->r)) {
  912. // color chunk is invalid. Simply ignore it
  913. ASSIMP_LOG_ERROR("3DS: Unable to read EMISSIVE chunk");
  914. pc->r = pc->g = pc->b = 0.0f;
  915. }
  916. } break;
  917. case Discreet3DS::CHUNK_MAT_TRANSPARENCY: {
  918. // This is the material's transparency
  919. ai_real *pcf = &mScene->mMaterials.back().mTransparency;
  920. *pcf = ParsePercentageChunk();
  921. // NOTE: transparency, not opacity
  922. if (is_qnan(*pcf))
  923. *pcf = ai_real(1.0);
  924. else
  925. *pcf = ai_real(1.0) - *pcf * (ai_real)0xFFFF / ai_real(100.0);
  926. } break;
  927. case Discreet3DS::CHUNK_MAT_SHADING:
  928. // This is the material shading mode
  929. mScene->mMaterials.back().mShading = (D3DS::Discreet3DS::shadetype3ds)stream->GetI2();
  930. break;
  931. case Discreet3DS::CHUNK_MAT_TWO_SIDE:
  932. // This is the two-sided flag
  933. mScene->mMaterials.back().mTwoSided = true;
  934. break;
  935. case Discreet3DS::CHUNK_MAT_SHININESS: { // This is the shininess of the material
  936. ai_real *pcf = &mScene->mMaterials.back().mSpecularExponent;
  937. *pcf = ParsePercentageChunk();
  938. if (is_qnan(*pcf))
  939. *pcf = 0.0;
  940. else
  941. *pcf *= (ai_real)0xFFFF;
  942. } break;
  943. case Discreet3DS::CHUNK_MAT_SHININESS_PERCENT: { // This is the shininess strength of the material
  944. ai_real *pcf = &mScene->mMaterials.back().mShininessStrength;
  945. *pcf = ParsePercentageChunk();
  946. if (is_qnan(*pcf))
  947. *pcf = ai_real(0.0);
  948. else
  949. *pcf *= (ai_real)0xffff / ai_real(100.0);
  950. } break;
  951. case Discreet3DS::CHUNK_MAT_SELF_ILPCT: { // This is the self illumination strength of the material
  952. ai_real f = ParsePercentageChunk();
  953. if (is_qnan(f))
  954. f = ai_real(0.0);
  955. else
  956. f *= (ai_real)0xFFFF / ai_real(100.0);
  957. mScene->mMaterials.back().mEmissive = aiColor3D(f, f, f);
  958. } break;
  959. // Parse texture chunks
  960. case Discreet3DS::CHUNK_MAT_TEXTURE:
  961. // Diffuse texture
  962. ParseTextureChunk(&mScene->mMaterials.back().sTexDiffuse);
  963. break;
  964. case Discreet3DS::CHUNK_MAT_BUMPMAP:
  965. // Height map
  966. ParseTextureChunk(&mScene->mMaterials.back().sTexBump);
  967. break;
  968. case Discreet3DS::CHUNK_MAT_OPACMAP:
  969. // Opacity texture
  970. ParseTextureChunk(&mScene->mMaterials.back().sTexOpacity);
  971. break;
  972. case Discreet3DS::CHUNK_MAT_MAT_SHINMAP:
  973. // Shininess map
  974. ParseTextureChunk(&mScene->mMaterials.back().sTexShininess);
  975. break;
  976. case Discreet3DS::CHUNK_MAT_SPECMAP:
  977. // Specular map
  978. ParseTextureChunk(&mScene->mMaterials.back().sTexSpecular);
  979. break;
  980. case Discreet3DS::CHUNK_MAT_SELFIMAP:
  981. // Self-illumination (emissive) map
  982. ParseTextureChunk(&mScene->mMaterials.back().sTexEmissive);
  983. break;
  984. case Discreet3DS::CHUNK_MAT_REFLMAP:
  985. // Reflection map
  986. ParseTextureChunk(&mScene->mMaterials.back().sTexReflective);
  987. break;
  988. };
  989. ASSIMP_3DS_END_CHUNK();
  990. }
  991. // ------------------------------------------------------------------------------------------------
  992. void Discreet3DSImporter::ParseTextureChunk(D3DS::Texture *pcOut) {
  993. ASSIMP_3DS_BEGIN_CHUNK();
  994. // get chunk type
  995. switch (chunk.Flag) {
  996. case Discreet3DS::CHUNK_MAPFILE: {
  997. // The material name string is already zero-terminated, but we need to be sure ...
  998. const char *sz = (const char *)stream->GetPtr();
  999. unsigned int cnt = 0;
  1000. while (stream->GetI1())
  1001. ++cnt;
  1002. pcOut->mMapName = std::string(sz, cnt);
  1003. } break;
  1004. case Discreet3DS::CHUNK_PERCENTD:
  1005. // Manually parse the blend factor
  1006. pcOut->mTextureBlend = ai_real(stream->GetF8());
  1007. break;
  1008. case Discreet3DS::CHUNK_PERCENTF:
  1009. // Manually parse the blend factor
  1010. pcOut->mTextureBlend = stream->GetF4();
  1011. break;
  1012. case Discreet3DS::CHUNK_PERCENTW:
  1013. // Manually parse the blend factor
  1014. pcOut->mTextureBlend = (ai_real)((uint16_t)stream->GetI2()) / ai_real(100.0);
  1015. break;
  1016. case Discreet3DS::CHUNK_MAT_MAP_USCALE:
  1017. // Texture coordinate scaling in the U direction
  1018. pcOut->mScaleU = stream->GetF4();
  1019. if (0.0f == pcOut->mScaleU) {
  1020. ASSIMP_LOG_WARN("Texture coordinate scaling in the x direction is zero. Assuming 1.");
  1021. pcOut->mScaleU = 1.0f;
  1022. }
  1023. break;
  1024. case Discreet3DS::CHUNK_MAT_MAP_VSCALE:
  1025. // Texture coordinate scaling in the V direction
  1026. pcOut->mScaleV = stream->GetF4();
  1027. if (0.0f == pcOut->mScaleV) {
  1028. ASSIMP_LOG_WARN("Texture coordinate scaling in the y direction is zero. Assuming 1.");
  1029. pcOut->mScaleV = 1.0f;
  1030. }
  1031. break;
  1032. case Discreet3DS::CHUNK_MAT_MAP_UOFFSET:
  1033. // Texture coordinate offset in the U direction
  1034. pcOut->mOffsetU = -stream->GetF4();
  1035. break;
  1036. case Discreet3DS::CHUNK_MAT_MAP_VOFFSET:
  1037. // Texture coordinate offset in the V direction
  1038. pcOut->mOffsetV = stream->GetF4();
  1039. break;
  1040. case Discreet3DS::CHUNK_MAT_MAP_ANG:
  1041. // Texture coordinate rotation, CCW in DEGREES
  1042. pcOut->mRotation = -AI_DEG_TO_RAD(stream->GetF4());
  1043. break;
  1044. case Discreet3DS::CHUNK_MAT_MAP_TILING: {
  1045. const uint16_t iFlags = stream->GetI2();
  1046. // Get the mapping mode (for both axes)
  1047. if (iFlags & 0x2u)
  1048. pcOut->mMapMode = aiTextureMapMode_Mirror;
  1049. else if (iFlags & 0x10u)
  1050. pcOut->mMapMode = aiTextureMapMode_Decal;
  1051. // wrapping in all remaining cases
  1052. else
  1053. pcOut->mMapMode = aiTextureMapMode_Wrap;
  1054. } break;
  1055. };
  1056. ASSIMP_3DS_END_CHUNK();
  1057. }
  1058. // ------------------------------------------------------------------------------------------------
  1059. // Read a percentage chunk
  1060. ai_real Discreet3DSImporter::ParsePercentageChunk() {
  1061. Discreet3DS::Chunk chunk;
  1062. ReadChunk(&chunk);
  1063. if (Discreet3DS::CHUNK_PERCENTF == chunk.Flag) {
  1064. return stream->GetF4() * ai_real(100) / ai_real(0xFFFF);
  1065. } else if (Discreet3DS::CHUNK_PERCENTW == chunk.Flag) {
  1066. return (ai_real)((uint16_t)stream->GetI2()) / (ai_real)0xFFFF;
  1067. }
  1068. return get_qnan();
  1069. }
  1070. // ------------------------------------------------------------------------------------------------
  1071. // Read a color chunk. If a percentage chunk is found instead it is read as a grayscale color
  1072. void Discreet3DSImporter::ParseColorChunk(aiColor3D *out, bool acceptPercent) {
  1073. ai_assert(out != nullptr);
  1074. // error return value
  1075. const ai_real qnan = get_qnan();
  1076. static const aiColor3D clrError = aiColor3D(qnan, qnan, qnan);
  1077. Discreet3DS::Chunk chunk;
  1078. ReadChunk(&chunk);
  1079. const unsigned int diff = chunk.Size - sizeof(Discreet3DS::Chunk);
  1080. bool bGamma = false;
  1081. // Get the type of the chunk
  1082. switch (chunk.Flag) {
  1083. case Discreet3DS::CHUNK_LINRGBF:
  1084. bGamma = true;
  1085. case Discreet3DS::CHUNK_RGBF:
  1086. if (sizeof(float) * 3 > diff) {
  1087. *out = clrError;
  1088. return;
  1089. }
  1090. out->r = stream->GetF4();
  1091. out->g = stream->GetF4();
  1092. out->b = stream->GetF4();
  1093. break;
  1094. case Discreet3DS::CHUNK_LINRGBB:
  1095. bGamma = true;
  1096. case Discreet3DS::CHUNK_RGBB: {
  1097. if (sizeof(char) * 3 > diff) {
  1098. *out = clrError;
  1099. return;
  1100. }
  1101. const ai_real invVal = ai_real(1.0) / ai_real(255.0);
  1102. out->r = (ai_real)(uint8_t)stream->GetI1() * invVal;
  1103. out->g = (ai_real)(uint8_t)stream->GetI1() * invVal;
  1104. out->b = (ai_real)(uint8_t)stream->GetI1() * invVal;
  1105. } break;
  1106. // Percentage chunks are accepted, too.
  1107. case Discreet3DS::CHUNK_PERCENTF:
  1108. if (acceptPercent && 4 <= diff) {
  1109. out->g = out->b = out->r = stream->GetF4();
  1110. break;
  1111. }
  1112. *out = clrError;
  1113. return;
  1114. case Discreet3DS::CHUNK_PERCENTW:
  1115. if (acceptPercent && 1 <= diff) {
  1116. out->g = out->b = out->r = (ai_real)(uint8_t)stream->GetI1() / ai_real(255.0);
  1117. break;
  1118. }
  1119. *out = clrError;
  1120. return;
  1121. default:
  1122. stream->IncPtr(diff);
  1123. // Skip unknown chunks, hope this won't cause any problems.
  1124. return ParseColorChunk(out, acceptPercent);
  1125. };
  1126. (void)bGamma;
  1127. }
  1128. #endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER