1 /*
2 * Copyright 2011-2019 Branimir Karadzic. All rights reserved.
3 * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
4 */
5
6 #include <algorithm>
7
8 #include <bx/string.h>
9 #include <bgfx/bgfx.h>
10 #include "../../src/vertexdecl.h"
11
12 #include <tinystl/allocator.h>
13 #include <tinystl/string.h>
14 #include <tinystl/vector.h>
15 namespace stl = tinystl;
16
17 #include <meshoptimizer/src/meshoptimizer.h>
18
19 #define BGFX_GEOMETRYC_VERSION_MAJOR 1
20 #define BGFX_GEOMETRYC_VERSION_MINOR 0
21
22 #if 0
23 # define BX_TRACE(_format, ...) \
24 do { \
25 bx::printf(BX_FILE_LINE_LITERAL "BGFX " _format "\n", ##__VA_ARGS__); \
26 } while(0)
27
28 # define BX_WARN(_condition, _format, ...) \
29 do { \
30 if (!(_condition) ) \
31 { \
32 BX_TRACE(BX_FILE_LINE_LITERAL "WARN " _format, ##__VA_ARGS__); \
33 } \
34 } while(0)
35
36 # define BX_CHECK(_condition, _format, ...) \
37 do { \
38 if (!(_condition) ) \
39 { \
40 BX_TRACE(BX_FILE_LINE_LITERAL "CHECK " _format, ##__VA_ARGS__); \
41 bx::debugBreak(); \
42 } \
43 } while(0)
44 #endif // 0
45
46 #include <bx/bx.h>
47 #include <bx/debug.h>
48 #include <bx/commandline.h>
49 #include <bx/timer.h>
50 #include <bx/hash.h>
51 #include <bx/uint32_t.h>
52 #include <bx/math.h>
53 #include <bx/file.h>
54
55 #include "bounds.h"
56
57 typedef stl::vector<bx::Vec3> Vec3Array;
58
59 struct Index3
60 {
61 int32_t m_position;
62 int32_t m_texcoord;
63 int32_t m_normal;
64 int32_t m_vbc; // Barycentric ID. Holds eigher 0, 1 or 2.
65 };
66
67 struct TriIndices
68 {
69 Index3 m_index[3];
70 };
71
72 typedef stl::vector<TriIndices> TriangleArray;
73
74 struct Group
75 {
76 uint32_t m_startTriangle;
77 uint32_t m_numTriangles;
78 stl::string m_name;
79 stl::string m_material;
80 };
81
82 typedef stl::vector<Group> GroupArray;
83
84 struct Primitive
85 {
86 uint32_t m_startVertex;
87 uint32_t m_startIndex;
88 uint32_t m_numVertices;
89 uint32_t m_numIndices;
90 stl::string m_name;
91 };
92
93 typedef stl::vector<Primitive> PrimitiveArray;
94
95 static uint32_t s_obbSteps = 17;
96
97 #define BGFX_CHUNK_MAGIC_VB BX_MAKEFOURCC('V', 'B', ' ', 0x1)
98 #define BGFX_CHUNK_MAGIC_VBC BX_MAKEFOURCC('V', 'B', 'C', 0x0)
99 #define BGFX_CHUNK_MAGIC_IB BX_MAKEFOURCC('I', 'B', ' ', 0x0)
100 #define BGFX_CHUNK_MAGIC_IBC BX_MAKEFOURCC('I', 'B', 'C', 0x1)
101 #define BGFX_CHUNK_MAGIC_PRI BX_MAKEFOURCC('P', 'R', 'I', 0x0)
102
optimizeVertexCache(uint16_t * _indices,uint32_t _numIndices,uint32_t _numVertices)103 void optimizeVertexCache(uint16_t* _indices, uint32_t _numIndices, uint32_t _numVertices)
104 {
105 uint16_t* newIndexList = new uint16_t[_numIndices];
106 meshopt_optimizeVertexCache(newIndexList, _indices, _numIndices, _numVertices);
107 bx::memCopy(_indices, newIndexList, _numIndices * 2);
108 delete[] newIndexList;
109 }
110
optimizeVertexFetch(uint16_t * _indices,uint32_t _numIndices,uint8_t * _vertexData,uint32_t _numVertices,uint16_t _stride)111 uint32_t optimizeVertexFetch(uint16_t* _indices, uint32_t _numIndices, uint8_t* _vertexData, uint32_t _numVertices, uint16_t _stride)
112 {
113 unsigned char* newVertices = (unsigned char*)malloc(_numVertices * _stride );
114 size_t vertexCount = meshopt_optimizeVertexFetch(newVertices, _indices, _numIndices, _vertexData, _numVertices, _stride);
115 bx::memCopy(_vertexData, newVertices, _numVertices * _stride);
116 free(newVertices);
117
118 return uint32_t(vertexCount);
119 }
120
writeCompressedIndices(bx::WriterI * _writer,const uint16_t * _indices,uint32_t _numIndices,uint32_t _numVertices)121 void writeCompressedIndices(bx::WriterI* _writer, const uint16_t* _indices, uint32_t _numIndices, uint32_t _numVertices)
122 {
123 size_t maxSize = meshopt_encodeIndexBufferBound(_numIndices, _numVertices);
124 unsigned char* compressedIndices = (unsigned char*)malloc(maxSize);
125 size_t compressedSize = meshopt_encodeIndexBuffer(compressedIndices, maxSize, _indices, _numIndices);
126 bx::printf( "indices uncompressed: %10d, compressed: %10d, ratio: %0.2f%%\n"
127 , _numIndices*2
128 , (uint32_t)compressedSize
129 , 100.0f - float(compressedSize ) / float(_numIndices*2)*100.0f
130 );
131
132 bx::write(_writer, (uint32_t)compressedSize);
133 bx::write(_writer, compressedIndices, (uint32_t)compressedSize );
134 free(compressedIndices);
135 }
136
writeCompressedVertices(bx::WriterI * _writer,const uint8_t * _vertices,uint32_t _numVertices,uint16_t _stride)137 void writeCompressedVertices(bx::WriterI* _writer, const uint8_t* _vertices, uint32_t _numVertices, uint16_t _stride)
138 {
139 size_t maxSize = meshopt_encodeVertexBufferBound(_numVertices, _stride);
140 unsigned char* compressedVertices = (unsigned char*)malloc(maxSize);
141 size_t compressedSize = meshopt_encodeVertexBuffer(compressedVertices, maxSize, _vertices, _numVertices, _stride);
142 bx::printf("vertices uncompressed: %10d, compressed: %10d, ratio: %0.2f%%\n"
143 , _numVertices * _stride
144 , (uint32_t)compressedSize
145 , 100.0f - float(compressedSize) / float(_numVertices * _stride)*100.0f
146 );
147
148 bx::write(_writer, (uint32_t)compressedSize);
149 bx::write(_writer, compressedVertices, (uint32_t)compressedSize );
150 free(compressedVertices);
151 }
152
calcTangents(void * _vertices,uint16_t _numVertices,bgfx::VertexLayout _layout,const uint16_t * _indices,uint32_t _numIndices)153 void calcTangents(void* _vertices, uint16_t _numVertices, bgfx::VertexLayout _layout, const uint16_t* _indices, uint32_t _numIndices)
154 {
155 struct PosTexcoord
156 {
157 float m_x;
158 float m_y;
159 float m_z;
160 float m_pad0;
161 float m_u;
162 float m_v;
163 float m_pad1;
164 float m_pad2;
165 };
166
167 float* tangents = new float[6*_numVertices];
168 bx::memSet(tangents, 0, 6*_numVertices*sizeof(float) );
169
170 PosTexcoord v0;
171 PosTexcoord v1;
172 PosTexcoord v2;
173
174 for (uint32_t ii = 0, num = _numIndices/3; ii < num; ++ii)
175 {
176 const uint16_t* indices = &_indices[ii*3];
177 uint32_t i0 = indices[0];
178 uint32_t i1 = indices[1];
179 uint32_t i2 = indices[2];
180
181 bgfx::vertexUnpack(&v0.m_x, bgfx::Attrib::Position, _layout, _vertices, i0);
182 bgfx::vertexUnpack(&v0.m_u, bgfx::Attrib::TexCoord0, _layout, _vertices, i0);
183
184 bgfx::vertexUnpack(&v1.m_x, bgfx::Attrib::Position, _layout, _vertices, i1);
185 bgfx::vertexUnpack(&v1.m_u, bgfx::Attrib::TexCoord0, _layout, _vertices, i1);
186
187 bgfx::vertexUnpack(&v2.m_x, bgfx::Attrib::Position, _layout, _vertices, i2);
188 bgfx::vertexUnpack(&v2.m_u, bgfx::Attrib::TexCoord0, _layout, _vertices, i2);
189
190 const float bax = v1.m_x - v0.m_x;
191 const float bay = v1.m_y - v0.m_y;
192 const float baz = v1.m_z - v0.m_z;
193 const float bau = v1.m_u - v0.m_u;
194 const float bav = v1.m_v - v0.m_v;
195
196 const float cax = v2.m_x - v0.m_x;
197 const float cay = v2.m_y - v0.m_y;
198 const float caz = v2.m_z - v0.m_z;
199 const float cau = v2.m_u - v0.m_u;
200 const float cav = v2.m_v - v0.m_v;
201
202 const float det = (bau * cav - bav * cau);
203 const float invDet = 1.0f / det;
204
205 const float tx = (bax * cav - cax * bav) * invDet;
206 const float ty = (bay * cav - cay * bav) * invDet;
207 const float tz = (baz * cav - caz * bav) * invDet;
208
209 const float bx = (cax * bau - bax * cau) * invDet;
210 const float by = (cay * bau - bay * cau) * invDet;
211 const float bz = (caz * bau - baz * cau) * invDet;
212
213 for (uint32_t jj = 0; jj < 3; ++jj)
214 {
215 float* tanu = &tangents[indices[jj]*6];
216 float* tanv = &tanu[3];
217 tanu[0] += tx;
218 tanu[1] += ty;
219 tanu[2] += tz;
220
221 tanv[0] += bx;
222 tanv[1] += by;
223 tanv[2] += bz;
224 }
225 }
226
227 for (uint32_t ii = 0; ii < _numVertices; ++ii)
228 {
229 const bx::Vec3 tanu = bx::load<bx::Vec3>(&tangents[ii*6]);
230 const bx::Vec3 tanv = bx::load<bx::Vec3>(&tangents[ii*6 + 3]);
231
232 float nxyzw[4];
233 bgfx::vertexUnpack(nxyzw, bgfx::Attrib::Normal, _layout, _vertices, ii);
234
235 const bx::Vec3 normal = bx::load<bx::Vec3>(nxyzw);
236 const float ndt = bx::dot(normal, tanu);
237 const bx::Vec3 nxt = bx::cross(normal, tanu);
238 const bx::Vec3 tmp = bx::sub(tanu, bx::mul(normal, ndt) );
239
240 float tangent[4];
241 bx::store(tangent, bx::normalize(tmp) );
242 tangent[3] = bx::dot(nxt, tanv) < 0.0f ? -1.0f : 1.0f;
243
244 bgfx::vertexPack(tangent, true, bgfx::Attrib::Tangent, _layout, _vertices, ii);
245 }
246
247 delete [] tangents;
248 }
249
write(bx::WriterI * _writer,const void * _vertices,uint32_t _numVertices,uint32_t _stride)250 void write(bx::WriterI* _writer, const void* _vertices, uint32_t _numVertices, uint32_t _stride)
251 {
252 Sphere maxSphere;
253 calcMaxBoundingSphere(maxSphere, _vertices, _numVertices, _stride);
254
255 Sphere minSphere;
256 calcMinBoundingSphere(minSphere, _vertices, _numVertices, _stride);
257
258 if (minSphere.radius > maxSphere.radius)
259 {
260 bx::write(_writer, maxSphere);
261 }
262 else
263 {
264 bx::write(_writer, minSphere);
265 }
266
267 Aabb aabb;
268 toAabb(aabb, _vertices, _numVertices, _stride);
269 bx::write(_writer, aabb);
270
271 Obb obb;
272 calcObb(obb, _vertices, _numVertices, _stride, s_obbSteps);
273 bx::write(_writer, obb);
274 }
275
write(bx::WriterI * _writer,const uint8_t * _vertices,uint32_t _numVertices,const bgfx::VertexLayout & _layout,const uint16_t * _indices,uint32_t _numIndices,bool _compress,const stl::string & _material,const PrimitiveArray & _primitives)276 void write(bx::WriterI* _writer
277 , const uint8_t* _vertices
278 , uint32_t _numVertices
279 , const bgfx::VertexLayout& _layout
280 , const uint16_t* _indices
281 , uint32_t _numIndices
282 , bool _compress
283 , const stl::string& _material
284 , const PrimitiveArray& _primitives
285 )
286 {
287 using namespace bx;
288 using namespace bgfx;
289
290 uint32_t stride = _layout.getStride();
291
292 if (_compress)
293 {
294 write(_writer, BGFX_CHUNK_MAGIC_VBC);
295 write(_writer, _vertices, _numVertices, stride);
296
297 write(_writer, _layout);
298
299 write(_writer, uint16_t(_numVertices) );
300 writeCompressedVertices(_writer, _vertices, _numVertices, uint16_t(stride));
301 }
302 else
303 {
304 write(_writer, BGFX_CHUNK_MAGIC_VB);
305 write(_writer, _vertices, _numVertices, stride);
306
307 write(_writer, _layout);
308
309 write(_writer, uint16_t(_numVertices) );
310 write(_writer, _vertices, _numVertices*stride);
311 }
312
313 if (_compress)
314 {
315 write(_writer, BGFX_CHUNK_MAGIC_IBC);
316 write(_writer, _numIndices);
317 writeCompressedIndices(_writer, _indices, _numIndices, _numVertices);
318 }
319 else
320 {
321 write(_writer, BGFX_CHUNK_MAGIC_IB);
322 write(_writer, _numIndices);
323 write(_writer, _indices, _numIndices*2);
324 }
325
326 write(_writer, BGFX_CHUNK_MAGIC_PRI);
327 uint16_t nameLen = uint16_t(_material.size() );
328 write(_writer, nameLen);
329 write(_writer, _material.c_str(), nameLen);
330 write(_writer, uint16_t(_primitives.size() ) );
331 for (PrimitiveArray::const_iterator primIt = _primitives.begin(); primIt != _primitives.end(); ++primIt)
332 {
333 const Primitive& prim = *primIt;
334 nameLen = uint16_t(prim.m_name.size() );
335 write(_writer, nameLen);
336 write(_writer, prim.m_name.c_str(), nameLen);
337 write(_writer, prim.m_startIndex);
338 write(_writer, prim.m_numIndices);
339 write(_writer, prim.m_startVertex);
340 write(_writer, prim.m_numVertices);
341 write(_writer, &_vertices[prim.m_startVertex*stride], prim.m_numVertices, stride);
342 }
343 }
344
rgbaToAbgr(uint8_t _r,uint8_t _g,uint8_t _b,uint8_t _a)345 inline uint32_t rgbaToAbgr(uint8_t _r, uint8_t _g, uint8_t _b, uint8_t _a)
346 {
347 return (uint32_t(_r)<<0)
348 | (uint32_t(_g)<<8)
349 | (uint32_t(_b)<<16)
350 | (uint32_t(_a)<<24)
351 ;
352 }
353
354 struct GroupSortByMaterial
355 {
operator ()GroupSortByMaterial356 bool operator()(const Group& _lhs, const Group& _rhs)
357 {
358 return 0 < bx::strCmp(_lhs.m_material.c_str(), _rhs.m_material.c_str() );
359 }
360 };
361
help(const char * _error=NULL)362 void help(const char* _error = NULL)
363 {
364 if (NULL != _error)
365 {
366 bx::printf("Error:\n%s\n\n", _error);
367 }
368
369 bx::printf(
370 "geometryc, bgfx geometry compiler tool, version %d.%d.%d.\n"
371 "Copyright 2011-2019 Branimir Karadzic. All rights reserved.\n"
372 "License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause\n\n"
373 , BGFX_GEOMETRYC_VERSION_MAJOR
374 , BGFX_GEOMETRYC_VERSION_MINOR
375 , BGFX_API_VERSION
376 );
377
378 bx::printf(
379 "Usage: geometryc -f <in> -o <out>\n"
380
381 "\n"
382 "Supported input file types:\n"
383 " *.obj Wavefront\n"
384
385 "\n"
386 "Options:\n"
387 " -h, --help Help.\n"
388 " -v, --version Version information only.\n"
389 " -f <file path> Input file path.\n"
390 " -o <file path> Output file path.\n"
391 " -s, --scale <num> Scale factor.\n"
392 " --ccw Counter-clockwise winding order.\n"
393 " --flipv Flip texture coordinate V.\n"
394 " --obb <num> Number of steps for calculating oriented bounding box.\n"
395 " Default value is 17. Less steps less precise OBB is.\n"
396 " More steps slower calculation.\n"
397 " --packnormal <num> Normal packing.\n"
398 " 0 - unpacked 12 bytes (default).\n"
399 " 1 - packed 4 bytes.\n"
400 " --packuv <num> Texture coordinate packing.\n"
401 " 0 - unpacked 8 bytes (default).\n"
402 " 1 - packed 4 bytes.\n"
403 " --tangent Calculate tangent vectors (packing mode is the same as normal).\n"
404 " --barycentric Adds barycentric vertex attribute (packed in bgfx::Attrib::Color1).\n"
405 " -c, --compress Compress indices.\n"
406
407 "\n"
408 "For additional information, see https://github.com/bkaradzic/bgfx\n"
409 );
410 }
411
main(int _argc,const char * _argv[])412 int main(int _argc, const char* _argv[])
413 {
414 bx::CommandLine cmdLine(_argc, _argv);
415
416 if (cmdLine.hasArg('v', "version") )
417 {
418 bx::printf(
419 "geometryc, bgfx geometry compiler tool, version %d.%d.%d.\n"
420 , BGFX_GEOMETRYC_VERSION_MAJOR
421 , BGFX_GEOMETRYC_VERSION_MINOR
422 , BGFX_API_VERSION
423 );
424 return bx::kExitSuccess;
425 }
426
427 if (cmdLine.hasArg('h', "help") )
428 {
429 help();
430 return bx::kExitFailure;
431 }
432
433 const char* filePath = cmdLine.findOption('f');
434 if (NULL == filePath)
435 {
436 help("Input file name must be specified.");
437 return bx::kExitFailure;
438 }
439
440 const char* outFilePath = cmdLine.findOption('o');
441 if (NULL == outFilePath)
442 {
443 help("Output file name must be specified.");
444 return bx::kExitFailure;
445 }
446
447 float scale = 1.0f;
448 const char* scaleArg = cmdLine.findOption('s', "scale");
449 if (NULL != scaleArg)
450 {
451 if (!bx::fromString(&scale, scaleArg))
452 {
453 scale = 1.0f;
454 }
455 }
456
457 bool compress = cmdLine.hasArg('c', "compress");
458
459 cmdLine.hasArg(s_obbSteps, '\0', "obb");
460 s_obbSteps = bx::uint32_min(bx::uint32_max(s_obbSteps, 1), 90);
461
462 uint32_t packNormal = 0;
463 cmdLine.hasArg(packNormal, '\0', "packnormal");
464
465 uint32_t packUv = 0;
466 cmdLine.hasArg(packUv, '\0', "packuv");
467
468 bool ccw = cmdLine.hasArg("ccw");
469 bool flipV = cmdLine.hasArg("flipv");
470 bool hasTangent = cmdLine.hasArg("tangent");
471 bool hasBc = cmdLine.hasArg("barycentric");
472
473 bx::FileReader fr;
474 if (!bx::open(&fr, filePath) )
475 {
476 bx::printf("Unable to open input file '%s'.", filePath);
477 exit(bx::kExitFailure);
478 }
479
480 int64_t parseElapsed = -bx::getHPCounter();
481 int64_t triReorderElapsed = 0;
482
483 uint32_t size = (uint32_t)bx::getSize(&fr);
484 char* data = new char[size+1];
485 size = bx::read(&fr, data, size);
486 data[size] = '\0';
487 bx::close(&fr);
488
489 // Reference(s):
490 // - Wavefront .obj file
491 // https://en.wikipedia.org/wiki/Wavefront_.obj_file
492
493 Vec3Array positions;
494 Vec3Array normals;
495 Vec3Array texcoords;
496 TriangleArray triangles;
497 GroupArray groups;
498
499 uint32_t num = 0;
500
501 Group group;
502 group.m_startTriangle = 0;
503 group.m_numTriangles = 0;
504
505 char commandLine[2048];
506 uint32_t len = sizeof(commandLine);
507 int argc;
508 char* argv[64];
509
510 for (bx::StringView next(data, size); !next.isEmpty(); )
511 {
512 next = bx::tokenizeCommandLine(next, commandLine, len, argc, argv, BX_COUNTOF(argv), '\n');
513
514 if (0 < argc)
515 {
516 if (0 == bx::strCmp(argv[0], "#") )
517 {
518 if (2 < argc
519 && 0 == bx::strCmp(argv[2], "polygons") )
520 {
521 }
522 }
523 else if (0 == bx::strCmp(argv[0], "f") )
524 {
525 TriIndices triangle;
526 bx::memSet(&triangle, 0, sizeof(TriIndices) );
527
528 const int numNormals = (int)normals.size();
529 const int numTexcoords = (int)texcoords.size();
530 const int numPositions = (int)positions.size();
531 for (uint32_t edge = 0, numEdges = argc-1; edge < numEdges; ++edge)
532 {
533 Index3 index;
534 index.m_texcoord = -1;
535 index.m_normal = -1;
536 if (hasBc)
537 {
538 index.m_vbc = edge < 3 ? edge : (1+(edge+1) )&1;
539 }
540 else
541 {
542 index.m_vbc = 0;
543 }
544
545 {
546 bx::StringView triplet(argv[edge + 1]);
547 bx::StringView vertex(triplet);
548 bx::StringView texcoord = bx::strFind(triplet, '/');
549 if (!texcoord.isEmpty())
550 {
551 vertex.set(vertex.getPtr(), texcoord.getPtr());
552
553 const bx::StringView normal = bx::strFind(bx::StringView(texcoord.getPtr() + 1, triplet.getTerm()), '/');
554 if (!normal.isEmpty())
555 {
556 int32_t nn;
557 bx::fromString(&nn, bx::StringView(normal.getPtr() + 1, triplet.getTerm()));
558 index.m_normal = (nn < 0) ? nn + numNormals : nn - 1;
559 }
560
561 texcoord.set(texcoord.getPtr() + 1, normal.getPtr());
562
563 // Reference(s):
564 // - Wavefront .obj file / Vertex normal indices without texture coordinate indices
565 // https://en.wikipedia.org/wiki/Wavefront_.obj_file#Vertex_Normal_Indices_Without_Texture_Coordinate_Indices
566 if (!texcoord.isEmpty())
567 {
568 int32_t tex;
569 bx::fromString(&tex, texcoord);
570 index.m_texcoord = (tex < 0) ? tex + numTexcoords : tex - 1;
571 }
572 }
573
574 int32_t pos;
575 bx::fromString(&pos, vertex);
576 index.m_position = (pos < 0) ? pos + numPositions : pos - 1;
577 }
578
579 switch (edge)
580 {
581 case 0: case 1: case 2:
582 triangle.m_index[edge] = index;
583 if (2 == edge)
584 {
585 if (ccw)
586 {
587 bx::swap(triangle.m_index[1], triangle.m_index[2]);
588 }
589 triangles.push_back(triangle);
590 }
591 break;
592
593 default:
594 if (ccw)
595 {
596 triangle.m_index[2] = triangle.m_index[1];
597 triangle.m_index[1] = index;
598 }
599 else
600 {
601 triangle.m_index[1] = triangle.m_index[2];
602 triangle.m_index[2] = index;
603 }
604
605 triangles.push_back(triangle);
606 break;
607 }
608 }
609 }
610 else if (0 == bx::strCmp(argv[0], "g") )
611 {
612 group.m_name = argv[1];
613 }
614 else if (*argv[0] == 'v')
615 {
616 group.m_numTriangles = (uint32_t)(triangles.size() ) - group.m_startTriangle;
617 if (0 < group.m_numTriangles)
618 {
619 groups.push_back(group);
620 group.m_startTriangle = (uint32_t)(triangles.size() );
621 group.m_numTriangles = 0;
622 }
623
624 if (0 == bx::strCmp(argv[0], "vn") )
625 {
626 bx::Vec3 normal;
627 bx::fromString(&normal.x, argv[1]);
628 bx::fromString(&normal.y, argv[2]);
629 bx::fromString(&normal.z, argv[3]);
630
631 normals.push_back(normal);
632 }
633 else if (0 == bx::strCmp(argv[0], "vp") )
634 {
635 static bool once = true;
636 if (once)
637 {
638 once = false;
639 bx::printf("warning: 'parameter space vertices' are unsupported.\n");
640 }
641 }
642 else if (0 == bx::strCmp(argv[0], "vt") )
643 {
644 bx::Vec3 texcoord;
645 texcoord.y = 0.0f;
646 texcoord.z = 0.0f;
647
648 bx::fromString(&texcoord.x, argv[1]);
649
650 switch (argc)
651 {
652 case 4:
653 bx::fromString(&texcoord.z, argv[3]);
654 BX_FALLTHROUGH;
655
656 case 3:
657 bx::fromString(&texcoord.y, argv[2]);
658 break;
659
660 default:
661 break;
662 }
663
664 texcoords.push_back(texcoord);
665 }
666 else
667 {
668 float px, py, pz, pw;
669 bx::fromString(&px, argv[1]);
670 bx::fromString(&py, argv[2]);
671 bx::fromString(&pz, argv[3]);
672
673 if (argc == 5 || argc == 8)
674 {
675 bx::fromString(&pw, argv[4]);
676 }
677 else
678 {
679 pw = 1.0f;
680 }
681
682 float invW = scale/pw;
683 px *= invW;
684 py *= invW;
685 pz *= invW;
686
687 bx::Vec3 pos;
688 pos.x = px;
689 pos.y = py;
690 pos.z = pz;
691
692 positions.push_back(pos);
693 }
694 }
695 else if (0 == bx::strCmp(argv[0], "usemtl") )
696 {
697 stl::string material(argv[1]);
698
699 if (0 != bx::strCmp(material.c_str(), group.m_material.c_str() ) )
700 {
701 group.m_numTriangles = (uint32_t)(triangles.size() ) - group.m_startTriangle;
702 if (0 < group.m_numTriangles)
703 {
704 groups.push_back(group);
705 group.m_startTriangle = (uint32_t)(triangles.size() );
706 group.m_numTriangles = 0;
707 }
708 }
709
710 group.m_material = material;
711 }
712 // unsupported tags
713 // else if (0 == bx::strCmp(argv[0], "mtllib") )
714 // {
715 // }
716 // else if (0 == bx::strCmp(argv[0], "o") )
717 // {
718 // }
719 // else if (0 == bx::strCmp(argv[0], "s") )
720 // {
721 // }
722 }
723
724 ++num;
725 }
726
727 group.m_numTriangles = (uint32_t)(triangles.size() ) - group.m_startTriangle;
728 if (0 < group.m_numTriangles)
729 {
730 groups.push_back(group);
731 group.m_startTriangle = (uint32_t)(triangles.size() );
732 group.m_numTriangles = 0;
733 }
734
735 delete [] data;
736
737 int64_t now = bx::getHPCounter();
738 parseElapsed += now;
739 int64_t convertElapsed = -now;
740
741 std::sort(groups.begin(), groups.end(), GroupSortByMaterial() );
742
743 bool hasColor = false;
744 bool hasNormal;
745 bool hasTexcoord;
746 {
747 TriangleArray::const_iterator it = triangles.begin();
748 hasNormal = -1 != it->m_index[0].m_normal;
749 hasTexcoord = -1 != it->m_index[0].m_texcoord;
750
751 if (!hasTexcoord)
752 {
753 for (TriangleArray::iterator jt = triangles.begin(), jtEnd = triangles.end(); jt != jtEnd && !hasTexcoord; ++jt)
754 {
755 for (uint32_t i = 0; i < 3; ++i)
756 {
757 hasTexcoord |= -1 != jt->m_index[i].m_texcoord;
758 }
759 }
760
761 if (hasTexcoord)
762 {
763 for (TriangleArray::iterator jt = triangles.begin(), jtEnd = triangles.end(); jt != jtEnd; ++jt)
764 {
765 for (uint32_t i = 0; i < 3; ++i)
766 {
767 jt->m_index[i].m_texcoord = -1 == jt->m_index[i].m_texcoord ? 0 : jt->m_index[i].m_texcoord;
768 }
769 }
770 }
771 }
772
773 if (!hasNormal)
774 {
775 for (TriangleArray::iterator jt = triangles.begin(), jtEnd = triangles.end(); jt != jtEnd && !hasNormal; ++jt)
776 {
777 for (uint32_t i = 0; i < 3; ++i)
778 {
779 hasNormal |= -1 != jt->m_index[i].m_normal;
780 }
781 }
782
783 if (hasNormal)
784 {
785 for (TriangleArray::iterator jt = triangles.begin(), jtEnd = triangles.end(); jt != jtEnd; ++jt)
786 {
787 for (uint32_t i = 0; i < 3; ++i)
788 {
789 jt->m_index[i].m_normal = -1 == jt->m_index[i].m_normal ? 0 : jt->m_index[i].m_normal;
790 }
791 }
792 }
793 }
794 }
795
796 bgfx::VertexLayout layout;
797 layout.begin();
798 layout.add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float);
799
800 if (hasColor)
801 {
802 layout.add(bgfx::Attrib::Color0, 4, bgfx::AttribType::Uint8, true);
803 }
804
805 if (hasBc)
806 {
807 layout.add(bgfx::Attrib::Color1, 4, bgfx::AttribType::Uint8, true);
808 }
809
810 if (hasTexcoord)
811 {
812 switch (packUv)
813 {
814 default:
815 case 0:
816 layout.add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float);
817 break;
818
819 case 1:
820 layout.add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Half);
821 break;
822 }
823 }
824
825 if (hasNormal)
826 {
827 hasTangent &= hasTexcoord;
828
829 switch (packNormal)
830 {
831 default:
832 case 0:
833 layout.add(bgfx::Attrib::Normal, 3, bgfx::AttribType::Float);
834 if (hasTangent)
835 {
836 layout.add(bgfx::Attrib::Tangent, 4, bgfx::AttribType::Float);
837 }
838 break;
839
840 case 1:
841 layout.add(bgfx::Attrib::Normal, 4, bgfx::AttribType::Uint8, true, true);
842 if (hasTangent)
843 {
844 layout.add(bgfx::Attrib::Tangent, 4, bgfx::AttribType::Uint8, true, true);
845 }
846 break;
847 }
848 }
849
850 layout.end();
851
852 uint32_t stride = layout.getStride();
853 uint8_t* vertexData = new uint8_t[triangles.size() * 3 * stride];
854 uint16_t* indexData = new uint16_t[triangles.size() * 3];
855 int32_t numVertices = 0;
856 int32_t numIndices = 0;
857
858 int32_t writtenPrimitives = 0;
859 int32_t writtenVertices = 0;
860 int32_t writtenIndices = 0;
861
862 uint8_t* vertices = vertexData;
863 uint16_t* indices = indexData;
864
865 const uint32_t tableSize = 65536 * 2;
866 const uint32_t hashmod = tableSize - 1;
867 uint32_t* table = new uint32_t[tableSize];
868 bx::memSet(table, 0xff, tableSize * sizeof(uint32_t));
869
870 stl::string material = groups.begin()->m_material;
871
872 PrimitiveArray primitives;
873
874 bx::FileWriter writer;
875 if (!bx::open(&writer, outFilePath) )
876 {
877 bx::printf("Unable to open output file '%s'.", outFilePath);
878 exit(bx::kExitFailure);
879 }
880
881 Primitive prim;
882 prim.m_startVertex = 0;
883 prim.m_startIndex = 0;
884
885 uint32_t positionOffset = layout.getOffset(bgfx::Attrib::Position);
886 uint32_t color0Offset = layout.getOffset(bgfx::Attrib::Color0);
887
888 Group sentinelGroup;
889 sentinelGroup.m_startTriangle = 0;
890 sentinelGroup.m_numTriangles = UINT32_MAX;
891 groups.push_back(sentinelGroup);
892
893 uint32_t ii = 0;
894 for (GroupArray::const_iterator groupIt = groups.begin(); groupIt != groups.end(); ++groupIt, ++ii)
895 {
896 bool sentinel = groupIt->m_startTriangle == 0 && groupIt->m_numTriangles == UINT32_MAX;
897 for (uint32_t tri = groupIt->m_startTriangle, end = tri + groupIt->m_numTriangles; tri < end; ++tri)
898 {
899 if (0 != bx::strCmp(material.c_str(), groupIt->m_material.c_str() )
900 || sentinel
901 || 65533 <= numVertices)
902 {
903 prim.m_numVertices = numVertices - prim.m_startVertex;
904 prim.m_numIndices = numIndices - prim.m_startIndex;
905 if (0 < prim.m_numVertices)
906 {
907 primitives.push_back(prim);
908 }
909
910 if (hasTangent)
911 {
912 calcTangents(vertexData, uint16_t(numVertices), layout, indexData, numIndices);
913 }
914
915 triReorderElapsed -= bx::getHPCounter();
916 for (PrimitiveArray::const_iterator primIt = primitives.begin(); primIt != primitives.end(); ++primIt)
917 {
918 const Primitive& prim1 = *primIt;
919 optimizeVertexCache(indexData + prim1.m_startIndex, prim1.m_numIndices, numVertices);
920 }
921 numVertices = optimizeVertexFetch(indexData, numIndices, vertexData, numVertices, uint16_t(stride));
922
923 triReorderElapsed += bx::getHPCounter();
924
925 write(&writer
926 , vertexData
927 , numVertices
928 , layout
929 , indexData
930 , numIndices
931 , compress
932 , material
933 , primitives
934 );
935 primitives.clear();
936
937 bx::memSet(table, 0xff, tableSize * sizeof(uint32_t));
938
939 ++writtenPrimitives;
940 writtenVertices += numVertices;
941 writtenIndices += numIndices;
942
943 vertices = vertexData;
944 indices = indexData;
945 numVertices = 0;
946 numIndices = 0;
947 prim.m_startVertex = 0;
948 prim.m_startIndex = 0;
949
950 material = groupIt->m_material;
951
952 if (sentinel)
953 break;
954 }
955
956 TriIndices& triangle = triangles[tri];
957 for (uint32_t edge = 0; edge < 3; ++edge)
958 {
959 Index3& index = triangle.m_index[edge];
960
961 float* position = (float*)(vertices + positionOffset);
962 bx::memCopy(position, &positions[index.m_position], 3*sizeof(float) );
963
964 if (hasColor)
965 {
966 uint32_t* color0 = (uint32_t*)(vertices + color0Offset);
967 *color0 = rgbaToAbgr(numVertices%255, numIndices%255, 0, 0xff);
968 }
969
970 if (hasBc)
971 {
972 const float bc[3] =
973 {
974 (index.m_vbc == 0) ? 1.0f : 0.0f,
975 (index.m_vbc == 1) ? 1.0f : 0.0f,
976 (index.m_vbc == 2) ? 1.0f : 0.0f,
977 };
978 bgfx::vertexPack(bc, true, bgfx::Attrib::Color1, layout, vertices);
979 }
980
981 if (hasTexcoord)
982 {
983 float uv[2];
984 bx::memCopy(uv, &texcoords[index.m_texcoord], 2*sizeof(float) );
985
986 if (flipV)
987 {
988 uv[1] = -uv[1];
989 }
990
991 bgfx::vertexPack(uv, true, bgfx::Attrib::TexCoord0, layout, vertices);
992 }
993
994 if (hasNormal)
995 {
996 float normal[4];
997 bx::store(normal, bx::normalize(bx::load<bx::Vec3>(&normals[index.m_normal]) ) );
998 normal[3] = 0.0f;
999 bgfx::vertexPack(normal, true, bgfx::Attrib::Normal, layout, vertices);
1000 }
1001
1002 uint32_t hash = bx::hash<bx::HashMurmur2A>(vertices, stride);
1003 size_t bucket = hash & hashmod;
1004 uint32_t vertexIndex = UINT32_MAX;
1005
1006 for (size_t probe = 0; probe <= hashmod; ++probe)
1007 {
1008 uint32_t& item = table[bucket];
1009
1010 if (item == ~0u)
1011 {
1012 vertices += stride;
1013 item = numVertices++;
1014 vertexIndex = item;
1015 break;
1016 }
1017
1018 if (0 == bx::memCmp(vertexData + item * stride, vertices, stride))
1019 {
1020 vertexIndex = item;
1021 break;
1022 }
1023
1024 bucket = (bucket + probe + 1) & hashmod;
1025 }
1026
1027 if ( vertexIndex == UINT32_MAX )
1028 {
1029 bx::printf("hash table insert failed");
1030 exit(bx::kExitFailure);
1031 }
1032
1033 *indices++ = (uint16_t)vertexIndex;
1034 ++numIndices;
1035 }
1036 }
1037
1038 prim.m_numVertices = numVertices - prim.m_startVertex;
1039 if (0 < prim.m_numVertices)
1040 {
1041 prim.m_numIndices = numIndices - prim.m_startIndex;
1042 prim.m_name = groupIt->m_name;
1043 primitives.push_back(prim);
1044 prim.m_startVertex = numVertices;
1045 prim.m_startIndex = numIndices;
1046 }
1047
1048 BX_TRACE("%3d: s %5d, n %5d, %s\n"
1049 , ii
1050 , groupIt->m_startTriangle
1051 , groupIt->m_numTriangles
1052 , groupIt->m_material.c_str()
1053 );
1054 }
1055
1056 BX_CHECK(0 == primitives.size(), "Not all primitives are written");
1057
1058 bx::printf("size: %d\n", uint32_t(bx::seek(&writer) ) );
1059 bx::close(&writer);
1060
1061 delete [] table;
1062 delete [] indexData;
1063 delete [] vertexData;
1064
1065 now = bx::getHPCounter();
1066 convertElapsed += now;
1067
1068 bx::printf("parse %f [s]\ntri reorder %f [s]\nconvert %f [s]\n# %d, g %d, p %d, v %d, i %d\n"
1069 , double(parseElapsed)/bx::getHPFrequency()
1070 , double(triReorderElapsed)/bx::getHPFrequency()
1071 , double(convertElapsed)/bx::getHPFrequency()
1072 , num
1073 , uint32_t(groups.size() )
1074 , writtenPrimitives
1075 , writtenVertices
1076 , writtenIndices
1077 );
1078
1079 return bx::kExitSuccess;
1080 }
1081