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