1/*
2Open Asset Import Library (assimp)
3----------------------------------------------------------------------
4
5Copyright (c) 2006-2017, assimp team
6
7All rights reserved.
8
9Redistribution and use of this software in source and binary forms,
10with or without modification, are permitted provided that the
11following conditions are met:
12
13* Redistributions of source code must retain the above
14 copyright notice, this list of conditions and the
15 following disclaimer.
16
17* Redistributions in binary form must reproduce the above
18 copyright notice, this list of conditions and the
19 following disclaimer in the documentation and/or other
20 materials provided with the distribution.
21
22* Neither the name of the assimp team, nor the names of its
23 contributors may be used to endorse or promote products
24 derived from this software without specific prior
25 written permission of the assimp team.
26
27THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39----------------------------------------------------------------------
40*/
41
42/** @file StandardShapes.cpp
43 * @brief Implementation of the StandardShapes class
44 *
45 * The primitive geometry data comes from
46 * http://geometrictools.com/Documentation/PlatonicSolids.pdf.
47 */
48
49#include "StandardShapes.h"
50#include "StringComparison.h"
51#include <stddef.h>
52#include <assimp/Defines.h>
53#include <assimp/mesh.h>
54
55namespace Assimp {
56
57
58# define ADD_TRIANGLE(n0,n1,n2) \
59 positions.push_back(n0); \
60 positions.push_back(n1); \
61 positions.push_back(n2);
62
63# define ADD_PENTAGON(n0,n1,n2,n3,n4) \
64 if (polygons) \
65 { \
66 positions.push_back(n0); \
67 positions.push_back(n1); \
68 positions.push_back(n2); \
69 positions.push_back(n3); \
70 positions.push_back(n4); \
71 } \
72 else \
73 { \
74 ADD_TRIANGLE(n0, n1, n2) \
75 ADD_TRIANGLE(n0, n2, n3) \
76 ADD_TRIANGLE(n0, n3, n4) \
77 }
78
79# define ADD_QUAD(n0,n1,n2,n3) \
80 if (polygons) \
81 { \
82 positions.push_back(n0); \
83 positions.push_back(n1); \
84 positions.push_back(n2); \
85 positions.push_back(n3); \
86 } \
87 else \
88 { \
89 ADD_TRIANGLE(n0, n1, n2) \
90 ADD_TRIANGLE(n0, n2, n3) \
91 }
92
93
94// ------------------------------------------------------------------------------------------------
95// Fast subdivision for a mesh whose verts have a magnitude of 1
96void Subdivide(std::vector<aiVector3D>& positions)
97{
98 // assume this to be constant - (fixme: must be 1.0? I think so)
99 const ai_real fl1 = positions[0].Length();
100
101 unsigned int origSize = (unsigned int)positions.size();
102 for (unsigned int i = 0 ; i < origSize ; i+=3)
103 {
104 aiVector3D& tv0 = positions[i];
105 aiVector3D& tv1 = positions[i+1];
106 aiVector3D& tv2 = positions[i+2];
107
108 aiVector3D a = tv0, b = tv1, c = tv2;
109 aiVector3D v1 = aiVector3D(a.x+b.x, a.y+b.y, a.z+b.z).Normalize()*fl1;
110 aiVector3D v2 = aiVector3D(a.x+c.x, a.y+c.y, a.z+c.z).Normalize()*fl1;
111 aiVector3D v3 = aiVector3D(b.x+c.x, b.y+c.y, b.z+c.z).Normalize()*fl1;
112
113 tv0 = v1; tv1 = v3; tv2 = v2; // overwrite the original
114 ADD_TRIANGLE(v1, v2, a);
115 ADD_TRIANGLE(v2, v3, c);
116 ADD_TRIANGLE(v3, v1, b);
117 }
118}
119
120// ------------------------------------------------------------------------------------------------
121// Construct a mesh from given vertex positions
122aiMesh* StandardShapes::MakeMesh(const std::vector<aiVector3D>& positions,
123 unsigned int numIndices)
124{
125 if (positions.empty() || !numIndices) return NULL;
126
127 // Determine which kinds of primitives the mesh consists of
128 aiMesh* out = new aiMesh();
129 switch (numIndices)
130 {
131 case 1:
132 out->mPrimitiveTypes = aiPrimitiveType_POINT;
133 break;
134 case 2:
135 out->mPrimitiveTypes = aiPrimitiveType_LINE;
136 break;
137 case 3:
138 out->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
139 break;
140 default:
141 out->mPrimitiveTypes = aiPrimitiveType_POLYGON;
142 break;
143 };
144
145 out->mNumFaces = (unsigned int)positions.size() / numIndices;
146 out->mFaces = new aiFace[out->mNumFaces];
147 for (unsigned int i = 0, a = 0; i < out->mNumFaces;++i)
148 {
149 aiFace& f = out->mFaces[i];
150 f.mNumIndices = numIndices;
151 f.mIndices = new unsigned int[numIndices];
152 for (unsigned int i = 0; i < numIndices;++i,++a)
153 f.mIndices[i] = a;
154 }
155 out->mNumVertices = (unsigned int)positions.size();
156 out->mVertices = new aiVector3D[out->mNumVertices];
157 ::memcpy(out->mVertices,&positions[0],out->mNumVertices*sizeof(aiVector3D));
158 return out;
159}
160
161// ------------------------------------------------------------------------------------------------
162// Construct a mesh with a specific shape (callback)
163aiMesh* StandardShapes::MakeMesh ( unsigned int (*GenerateFunc)(
164 std::vector<aiVector3D>&))
165{
166 std::vector<aiVector3D> temp;
167 unsigned num = (*GenerateFunc)(temp);
168 return MakeMesh(temp,num);
169}
170
171// ------------------------------------------------------------------------------------------------
172// Construct a mesh with a specific shape (callback)
173aiMesh* StandardShapes::MakeMesh ( unsigned int (*GenerateFunc)(
174 std::vector<aiVector3D>&, bool))
175{
176 std::vector<aiVector3D> temp;
177 unsigned num = (*GenerateFunc)(temp,true);
178 return MakeMesh(temp,num);
179}
180
181// ------------------------------------------------------------------------------------------------
182// Construct a mesh with a specific shape (callback)
183aiMesh* StandardShapes::MakeMesh (unsigned int num, void (*GenerateFunc)(
184 unsigned int,std::vector<aiVector3D>&))
185{
186 std::vector<aiVector3D> temp;
187 (*GenerateFunc)(num,temp);
188 return MakeMesh(temp,3);
189}
190
191// ------------------------------------------------------------------------------------------------
192// Build an incosahedron with points.magnitude == 1
193unsigned int StandardShapes::MakeIcosahedron(std::vector<aiVector3D>& positions)
194{
195 positions.reserve(positions.size()+60);
196
197 const ai_real t = ( ai_real( 1.0 )+ ai_real( 2.236067977 ) ) / ai_real( 2.0 );
198 const ai_real s = std::sqrt(ai_real(1.0) + t*t);
199
200 const aiVector3D v0 = aiVector3D(t,1.0, 0.0)/s;
201 const aiVector3D v1 = aiVector3D(-t,1.0, 0.0)/s;
202 const aiVector3D v2 = aiVector3D(t,-1.0, 0.0)/s;
203 const aiVector3D v3 = aiVector3D(-t,-1.0, 0.0)/s;
204 const aiVector3D v4 = aiVector3D(1.0, 0.0, t)/s;
205 const aiVector3D v5 = aiVector3D(1.0, 0.0,-t)/s;
206 const aiVector3D v6 = aiVector3D(-1.0, 0.0,t)/s;
207 const aiVector3D v7 = aiVector3D(-1.0, 0.0,-t)/s;
208 const aiVector3D v8 = aiVector3D(0.0, t, 1.0)/s;
209 const aiVector3D v9 = aiVector3D(0.0,-t, 1.0)/s;
210 const aiVector3D v10 = aiVector3D(0.0, t,-1.0)/s;
211 const aiVector3D v11 = aiVector3D(0.0,-t,-1.0)/s;
212
213 ADD_TRIANGLE(v0,v8,v4);
214 ADD_TRIANGLE(v0,v5,v10);
215 ADD_TRIANGLE(v2,v4,v9);
216 ADD_TRIANGLE(v2,v11,v5);
217
218 ADD_TRIANGLE(v1,v6,v8);
219 ADD_TRIANGLE(v1,v10,v7);
220 ADD_TRIANGLE(v3,v9,v6);
221 ADD_TRIANGLE(v3,v7,v11);
222
223 ADD_TRIANGLE(v0,v10,v8);
224 ADD_TRIANGLE(v1,v8,v10);
225 ADD_TRIANGLE(v2,v9,v11);
226 ADD_TRIANGLE(v3,v11,v9);
227
228 ADD_TRIANGLE(v4,v2,v0);
229 ADD_TRIANGLE(v5,v0,v2);
230 ADD_TRIANGLE(v6,v1,v3);
231 ADD_TRIANGLE(v7,v3,v1);
232
233 ADD_TRIANGLE(v8,v6,v4);
234 ADD_TRIANGLE(v9,v4,v6);
235 ADD_TRIANGLE(v10,v5,v7);
236 ADD_TRIANGLE(v11,v7,v5);
237 return 3;
238}
239
240// ------------------------------------------------------------------------------------------------
241// Build a dodecahedron with points.magnitude == 1
242unsigned int StandardShapes::MakeDodecahedron(std::vector<aiVector3D>& positions,
243 bool polygons /*= false*/)
244{
245 positions.reserve(positions.size()+108);
246
247 const ai_real a = ai_real( 1.0 ) / ai_real(1.7320508);
248 const ai_real b = std::sqrt(( ai_real( 3.0 )- ai_real( 2.23606797))/ ai_real( 6.0) );
249 const ai_real c = std::sqrt(( ai_real( 3.0 )+ ai_real( 2.23606797f))/ ai_real( 6.0) );
250
251 const aiVector3D v0 = aiVector3D(a,a,a);
252 const aiVector3D v1 = aiVector3D(a,a,-a);
253 const aiVector3D v2 = aiVector3D(a,-a,a);
254 const aiVector3D v3 = aiVector3D(a,-a,-a);
255 const aiVector3D v4 = aiVector3D(-a,a,a);
256 const aiVector3D v5 = aiVector3D(-a,a,-a);
257 const aiVector3D v6 = aiVector3D(-a,-a,a);
258 const aiVector3D v7 = aiVector3D(-a,-a,-a);
259 const aiVector3D v8 = aiVector3D(b,c,0.0);
260 const aiVector3D v9 = aiVector3D(-b,c,0.0);
261 const aiVector3D v10 = aiVector3D(b,-c,0.0);
262 const aiVector3D v11 = aiVector3D(-b,-c,0.0);
263 const aiVector3D v12 = aiVector3D(c, 0.0, b);
264 const aiVector3D v13 = aiVector3D(c, 0.0, -b);
265 const aiVector3D v14 = aiVector3D(-c, 0.0, b);
266 const aiVector3D v15 = aiVector3D(-c, 0.0, -b);
267 const aiVector3D v16 = aiVector3D(0.0, b, c);
268 const aiVector3D v17 = aiVector3D(0.0, -b, c);
269 const aiVector3D v18 = aiVector3D(0.0, b, -c);
270 const aiVector3D v19 = aiVector3D(0.0, -b, -c);
271
272 ADD_PENTAGON(v0, v8, v9, v4, v16);
273 ADD_PENTAGON(v0, v12, v13, v1, v8);
274 ADD_PENTAGON(v0, v16, v17, v2, v12);
275 ADD_PENTAGON(v8, v1, v18, v5, v9);
276 ADD_PENTAGON(v12, v2, v10, v3, v13);
277 ADD_PENTAGON(v16, v4, v14, v6, v17);
278 ADD_PENTAGON(v9, v5, v15, v14, v4);
279
280 ADD_PENTAGON(v6, v11, v10, v2, v17);
281 ADD_PENTAGON(v3, v19, v18, v1, v13);
282 ADD_PENTAGON(v7, v15, v5, v18, v19);
283 ADD_PENTAGON(v7, v11, v6, v14, v15);
284 ADD_PENTAGON(v7, v19, v3, v10, v11);
285 return (polygons ? 5 : 3);
286}
287
288// ------------------------------------------------------------------------------------------------
289// Build an octahedron with points.magnitude == 1
290unsigned int StandardShapes::MakeOctahedron(std::vector<aiVector3D>& positions)
291{
292 positions.reserve(positions.size()+24);
293
294 const aiVector3D v0 = aiVector3D(1.0, 0.0, 0.0) ;
295 const aiVector3D v1 = aiVector3D(-1.0, 0.0, 0.0);
296 const aiVector3D v2 = aiVector3D(0.0, 1.0, 0.0);
297 const aiVector3D v3 = aiVector3D(0.0, -1.0, 0.0);
298 const aiVector3D v4 = aiVector3D(0.0, 0.0, 1.0);
299 const aiVector3D v5 = aiVector3D(0.0, 0.0, -1.0);
300
301 ADD_TRIANGLE(v4,v0,v2);
302 ADD_TRIANGLE(v4,v2,v1);
303 ADD_TRIANGLE(v4,v1,v3);
304 ADD_TRIANGLE(v4,v3,v0);
305
306 ADD_TRIANGLE(v5,v2,v0);
307 ADD_TRIANGLE(v5,v1,v2);
308 ADD_TRIANGLE(v5,v3,v1);
309 ADD_TRIANGLE(v5,v0,v3);
310 return 3;
311}
312
313// ------------------------------------------------------------------------------------------------
314// Build a tetrahedron with points.magnitude == 1
315unsigned int StandardShapes::MakeTetrahedron(std::vector<aiVector3D>& positions)
316{
317 positions.reserve(positions.size()+9);
318
319 const ai_real invThree = ai_real( 1.0 ) / ai_real( 3.0 );
320 const ai_real a = ai_real( 1.41421 ) * invThree;
321 const ai_real b = ai_real( 2.4494 ) * invThree;
322
323 const aiVector3D v0 = aiVector3D(0.0,0.0,1.0);
324 const aiVector3D v1 = aiVector3D(2*a,0,-invThree );
325 const aiVector3D v2 = aiVector3D(-a,b,-invThree );
326 const aiVector3D v3 = aiVector3D(-a,-b,-invThree );
327
328 ADD_TRIANGLE(v0,v1,v2);
329 ADD_TRIANGLE(v0,v2,v3);
330 ADD_TRIANGLE(v0,v3,v1);
331 ADD_TRIANGLE(v1,v3,v2);
332 return 3;
333}
334
335// ------------------------------------------------------------------------------------------------
336// Build a hexahedron with points.magnitude == 1
337unsigned int StandardShapes::MakeHexahedron(std::vector<aiVector3D>& positions,
338 bool polygons /*= false*/)
339{
340 positions.reserve(positions.size()+36);
341 const ai_real length = ai_real(1.0)/ai_real(1.73205080);
342
343 const aiVector3D v0 = aiVector3D(-1.0,-1.0,-1.0)*length;
344 const aiVector3D v1 = aiVector3D(1.0,-1.0,-1.0)*length;
345 const aiVector3D v2 = aiVector3D(1.0,1.0,-1.0)*length;
346 const aiVector3D v3 = aiVector3D(-1.0,1.0,-1.0)*length;
347 const aiVector3D v4 = aiVector3D(-1.0,-1.0,1.0)*length;
348 const aiVector3D v5 = aiVector3D(1.0,-1.0,1.0)*length;
349 const aiVector3D v6 = aiVector3D(1.0,1.0,1.0)*length;
350 const aiVector3D v7 = aiVector3D(-1.0,1.0,1.0)*length;
351
352 ADD_QUAD(v0,v3,v2,v1);
353 ADD_QUAD(v0,v1,v5,v4);
354 ADD_QUAD(v0,v4,v7,v3);
355 ADD_QUAD(v6,v5,v1,v2);
356 ADD_QUAD(v6,v2,v3,v7);
357 ADD_QUAD(v6,v7,v4,v5);
358 return (polygons ? 4 : 3);
359}
360
361// Cleanup ...
362#undef ADD_TRIANGLE
363#undef ADD_QUAD
364#undef ADD_PENTAGON
365
366// ------------------------------------------------------------------------------------------------
367// Create a subdivision sphere
368void StandardShapes::MakeSphere(unsigned int tess,
369 std::vector<aiVector3D>& positions)
370{
371 // Reserve enough storage. Every subdivision
372 // splits each triangle in 4, the icosahedron consists of 60 verts
373 positions.reserve(positions.size()+60 * integer_pow(4, tess));
374
375 // Construct an icosahedron to start with
376 MakeIcosahedron(positions);
377
378 // ... and subdivide it until the requested output
379 // tesselation is reached
380 for (unsigned int i = 0; i<tess;++i)
381 Subdivide(positions);
382}
383
384// ------------------------------------------------------------------------------------------------
385// Build a cone
386void StandardShapes::MakeCone(ai_real height,ai_real radius1,
387 ai_real radius2,unsigned int tess,
388 std::vector<aiVector3D>& positions,bool bOpen /*= false */)
389{
390 // Sorry, a cone with less than 3 segments makes ABSOLUTELY NO SENSE
391 if (tess < 3 || !height)
392 return;
393
394 size_t old = positions.size();
395
396 // No negative radii
397 radius1 = std::fabs(radius1);
398 radius2 = std::fabs(radius2);
399
400 ai_real halfHeight = height / ai_real(2.0);
401
402 // radius1 is always the smaller one
403 if (radius2 > radius1)
404 {
405 std::swap(radius2,radius1);
406 halfHeight = -halfHeight;
407 }
408 else old = SIZE_MAX;
409
410 // Use a large epsilon to check whether the cone is pointy
411 if (radius1 < (radius2-radius1)*10e-3)radius1 = 0.0;
412
413 // We will need 3*2 verts per segment + 3*2 verts per segment
414 // if the cone is closed
415 const unsigned int mem = tess*6 + (!bOpen ? tess*3 * (radius1 ? 2 : 1) : 0);
416 positions.reserve(positions.size () + mem);
417
418 // Now construct all segments
419 const ai_real angle_delta = (ai_real)AI_MATH_TWO_PI / tess;
420 const ai_real angle_max = (ai_real)AI_MATH_TWO_PI;
421
422 ai_real s = 1.0; // std::cos(angle == 0);
423 ai_real t = 0.0; // std::sin(angle == 0);
424
425 for (ai_real angle = 0.0; angle < angle_max; )
426 {
427 const aiVector3D v1 = aiVector3D (s * radius1, -halfHeight, t * radius1 );
428 const aiVector3D v2 = aiVector3D (s * radius2, halfHeight, t * radius2 );
429
430 const ai_real next = angle + angle_delta;
431 ai_real s2 = std::cos(next);
432 ai_real t2 = std::sin(next);
433
434 const aiVector3D v3 = aiVector3D (s2 * radius2, halfHeight, t2 * radius2 );
435 const aiVector3D v4 = aiVector3D (s2 * radius1, -halfHeight, t2 * radius1 );
436
437 positions.push_back(v1);
438 positions.push_back(v2);
439 positions.push_back(v3);
440 positions.push_back(v4);
441 positions.push_back(v1);
442 positions.push_back(v3);
443
444 if (!bOpen)
445 {
446 // generate the end 'cap'
447 positions.push_back(aiVector3D(s * radius2, halfHeight, t * radius2 ));
448 positions.push_back(aiVector3D(s2 * radius2, halfHeight, t2 * radius2 ));
449 positions.push_back(aiVector3D(0.0, halfHeight, 0.0));
450
451
452 if (radius1)
453 {
454 // generate the other end 'cap'
455 positions.push_back(aiVector3D(s * radius1, -halfHeight, t * radius1 ));
456 positions.push_back(aiVector3D(s2 * radius1, -halfHeight, t2 * radius1 ));
457 positions.push_back(aiVector3D(0.0, -halfHeight, 0.0));
458
459 }
460 }
461 s = s2;
462 t = t2;
463 angle = next;
464 }
465
466 // Need to flip face order?
467 if ( SIZE_MAX != old ) {
468 for (size_t s = old; s < positions.size();s += 3) {
469 std::swap(positions[s],positions[s+1]);
470 }
471 }
472}
473
474// ------------------------------------------------------------------------------------------------
475// Build a circle
476void StandardShapes::MakeCircle(ai_real radius, unsigned int tess,
477 std::vector<aiVector3D>& positions)
478{
479 // Sorry, a circle with less than 3 segments makes ABSOLUTELY NO SENSE
480 if (tess < 3 || !radius)
481 return;
482
483 radius = std::fabs(radius);
484
485 // We will need 3 vertices per segment
486 positions.reserve(positions.size()+tess*3);
487
488 const ai_real angle_delta = (ai_real)AI_MATH_TWO_PI / tess;
489 const ai_real angle_max = (ai_real)AI_MATH_TWO_PI;
490
491 ai_real s = 1.0; // std::cos(angle == 0);
492 ai_real t = 0.0; // std::sin(angle == 0);
493
494 for (ai_real angle = 0.0; angle < angle_max; )
495 {
496 positions.push_back(aiVector3D(s * radius,0.0,t * radius));
497 angle += angle_delta;
498 s = std::cos(angle);
499 t = std::sin(angle);
500 positions.push_back(aiVector3D(s * radius,0.0,t * radius));
501
502 positions.push_back(aiVector3D(0.0,0.0,0.0));
503 }
504}
505
506} // ! Assimp
507