goat3d

view libs/openctm/openctm.c @ 88:7941e89798e5

selections
author John Tsiombikas <nuclear@member.fsf.org>
date Thu, 15 May 2014 06:52:01 +0300
parents
children 21319e71117f
line source
1 //-----------------------------------------------------------------------------
2 // Product: OpenCTM
3 // File: openctm.c
4 // Description: API functions.
5 //-----------------------------------------------------------------------------
6 // Copyright (c) 2009-2010 Marcus Geelnard
7 //
8 // This software is provided 'as-is', without any express or implied
9 // warranty. In no event will the authors be held liable for any damages
10 // arising from the use of this software.
11 //
12 // Permission is granted to anyone to use this software for any purpose,
13 // including commercial applications, and to alter it and redistribute it
14 // freely, subject to the following restrictions:
15 //
16 // 1. The origin of this software must not be misrepresented; you must not
17 // claim that you wrote the original software. If you use this software
18 // in a product, an acknowledgment in the product documentation would be
19 // appreciated but is not required.
20 //
21 // 2. Altered source versions must be plainly marked as such, and must not
22 // be misrepresented as being the original software.
23 //
24 // 3. This notice may not be removed or altered from any source
25 // distribution.
26 //-----------------------------------------------------------------------------
28 #include <stdlib.h>
29 #include <string.h>
30 #include <stdio.h>
31 #include <math.h>
32 #include "openctm.h"
33 #include "internal.h"
36 // The C99 macro isfinite() is not supported on all platforms (specifically,
37 // MS Visual Studio does not support C99)
38 #if !defined(isfinite) && defined(_MSC_VER)
39 #include <float.h>
40 #define isfinite(x) _finite(x)
41 #endif
44 //-----------------------------------------------------------------------------
45 // _ctmFreeMapList() - Free a float map list.
46 //-----------------------------------------------------------------------------
47 static void _ctmFreeMapList(_CTMcontext * self, _CTMfloatmap * aMapList)
48 {
49 _CTMfloatmap * map, * nextMap;
50 map = aMapList;
51 while(map)
52 {
53 // Free internally allocated array (if we are in import mode)
54 if((self->mMode == CTM_IMPORT) && map->mValues)
55 free(map->mValues);
57 // Free map name
58 if(map->mName)
59 free(map->mName);
61 // Free file name
62 if(map->mFileName)
63 free(map->mFileName);
65 nextMap = map->mNext;
66 free(map);
67 map = nextMap;
68 }
69 }
71 //-----------------------------------------------------------------------------
72 // _ctmClearMesh() - Clear the mesh in a CTM context.
73 //-----------------------------------------------------------------------------
74 static void _ctmClearMesh(_CTMcontext * self)
75 {
76 // Free internally allocated mesh arrays
77 if(self->mMode == CTM_IMPORT)
78 {
79 if(self->mVertices)
80 free(self->mVertices);
81 if(self->mIndices)
82 free(self->mIndices);
83 if(self->mNormals)
84 free(self->mNormals);
85 }
87 // Clear externally assigned mesh arrays
88 self->mVertices = (CTMfloat *) 0;
89 self->mVertexCount = 0;
90 self->mIndices = (CTMuint *) 0;
91 self->mTriangleCount = 0;
92 self->mNormals = (CTMfloat *) 0;
94 // Free UV coordinate map list
95 _ctmFreeMapList(self, self->mUVMaps);
96 self->mUVMaps = (_CTMfloatmap *) 0;
97 self->mUVMapCount = 0;
99 // Free attribute map list
100 _ctmFreeMapList(self, self->mAttribMaps);
101 self->mAttribMaps = (_CTMfloatmap *) 0;
102 self->mAttribMapCount = 0;
103 }
105 //-----------------------------------------------------------------------------
106 // _ctmCheckMeshIntegrity() - Check if a mesh is valid (i.e. is non-empty, and
107 // contains valid data).
108 //-----------------------------------------------------------------------------
110 static CTMint _ctmCheckMeshIntegrity(_CTMcontext * self)
111 {
112 CTMuint i;
113 _CTMfloatmap * map;
115 // Check that we have all the mandatory data
116 if(!self->mVertices || !self->mIndices || (self->mVertexCount < 1) ||
117 (self->mTriangleCount < 1))
118 {
119 return CTM_FALSE;
120 }
122 // Check that all indices are within range
123 for(i = 0; i < (self->mTriangleCount * 3); ++ i)
124 {
125 if(self->mIndices[i] >= self->mVertexCount)
126 {
127 return CTM_FALSE;
128 }
129 }
131 // Check that all vertices are finite (non-NaN, non-inf)
132 for(i = 0; i < self->mVertexCount * 3; ++ i)
133 {
134 if(!isfinite(self->mVertices[i]))
135 {
136 return CTM_FALSE;
137 }
138 }
140 // Check that all normals are finite (non-NaN, non-inf)
141 if(self->mNormals)
142 {
143 for(i = 0; i < self->mVertexCount * 3; ++ i)
144 {
145 if(!isfinite(self->mNormals[i]))
146 {
147 return CTM_FALSE;
148 }
149 }
150 }
152 // Check that all UV maps are finite (non-NaN, non-inf)
153 map = self->mUVMaps;
154 while(map)
155 {
156 for(i = 0; i < self->mVertexCount * 2; ++ i)
157 {
158 if(!isfinite(map->mValues[i]))
159 {
160 return CTM_FALSE;
161 }
162 }
163 map = map->mNext;
164 }
166 // Check that all attribute maps are finite (non-NaN, non-inf)
167 map = self->mAttribMaps;
168 while(map)
169 {
170 for(i = 0; i < self->mVertexCount * 4; ++ i)
171 {
172 if(!isfinite(map->mValues[i]))
173 {
174 return CTM_FALSE;
175 }
176 }
177 map = map->mNext;
178 }
180 return CTM_TRUE;
181 }
183 //-----------------------------------------------------------------------------
184 // ctmNewContext()
185 //-----------------------------------------------------------------------------
186 CTMEXPORT CTMcontext CTMCALL ctmNewContext(CTMenum aMode)
187 {
188 _CTMcontext * self;
190 // Allocate memory for the new structure
191 self = (_CTMcontext *) malloc(sizeof(_CTMcontext));
193 // Initialize structure (set null pointers and zero array lengths)
194 memset(self, 0, sizeof(_CTMcontext));
195 self->mMode = aMode;
196 self->mError = CTM_NONE;
197 self->mMethod = CTM_METHOD_MG1;
198 self->mCompressionLevel = 1;
199 self->mVertexPrecision = 1.0f / 1024.0f;
200 self->mNormalPrecision = 1.0f / 256.0f;
202 return (CTMcontext) self;
203 }
205 //-----------------------------------------------------------------------------
206 // ctmFreeContext()
207 //-----------------------------------------------------------------------------
208 CTMEXPORT void CTMCALL ctmFreeContext(CTMcontext aContext)
209 {
210 _CTMcontext * self = (_CTMcontext *) aContext;
211 if(!self) return;
213 // Free all mesh resources
214 _ctmClearMesh(self);
216 // Free the file comment
217 if(self->mFileComment)
218 free(self->mFileComment);
220 // Free the context
221 free(self);
222 }
224 //-----------------------------------------------------------------------------
225 // ctmGetError()
226 //-----------------------------------------------------------------------------
227 CTMEXPORT CTMenum CTMCALL ctmGetError(CTMcontext aContext)
228 {
229 _CTMcontext * self = (_CTMcontext *) aContext;
230 CTMenum err;
232 if(!self) return CTM_INVALID_CONTEXT;
234 // Get error code and reset error state
235 err = self->mError;
236 self->mError = CTM_NONE;
237 return err;
238 }
240 //-----------------------------------------------------------------------------
241 // ctmErrorString()
242 //-----------------------------------------------------------------------------
243 CTMEXPORT const char * CTMCALL ctmErrorString(CTMenum aError)
244 {
245 switch(aError)
246 {
247 case CTM_INVALID_CONTEXT:
248 return "CTM_INVALID_CONTEXT";
249 case CTM_INVALID_ARGUMENT:
250 return "CTM_INVALID_ARGUMENT";
251 case CTM_INVALID_OPERATION:
252 return "CTM_INVALID_OPERATION";
253 case CTM_INVALID_MESH:
254 return "CTM_INVALID_MESH";
255 case CTM_OUT_OF_MEMORY:
256 return "CTM_OUT_OF_MEMORY";
257 case CTM_FILE_ERROR:
258 return "CTM_FILE_ERROR";
259 case CTM_BAD_FORMAT:
260 return "CTM_BAD_FORMAT";
261 case CTM_LZMA_ERROR:
262 return "CTM_LZMA_ERROR";
263 case CTM_INTERNAL_ERROR:
264 return "CTM_INTERNAL_ERROR";
265 case CTM_UNSUPPORTED_FORMAT_VERSION:
266 return "CTM_UNSUPPORTED_FORMAT_VERSION";
267 default:
268 return "Unknown error code";
269 }
270 }
272 //-----------------------------------------------------------------------------
273 // ctmGetInteger()
274 //-----------------------------------------------------------------------------
275 CTMEXPORT CTMuint CTMCALL ctmGetInteger(CTMcontext aContext, CTMenum aProperty)
276 {
277 _CTMcontext * self = (_CTMcontext *) aContext;
278 if(!self) return 0;
280 switch(aProperty)
281 {
282 case CTM_VERTEX_COUNT:
283 return self->mVertexCount;
285 case CTM_TRIANGLE_COUNT:
286 return self->mTriangleCount;
288 case CTM_UV_MAP_COUNT:
289 return self->mUVMapCount;
291 case CTM_ATTRIB_MAP_COUNT:
292 return self->mAttribMapCount;
294 case CTM_HAS_NORMALS:
295 return self->mNormals ? CTM_TRUE : CTM_FALSE;
297 case CTM_COMPRESSION_METHOD:
298 return (CTMuint) self->mMethod;
300 default:
301 self->mError = CTM_INVALID_ARGUMENT;
302 }
304 return 0;
305 }
307 //-----------------------------------------------------------------------------
308 // ctmGetFloat()
309 //-----------------------------------------------------------------------------
310 CTMEXPORT CTMfloat CTMCALL ctmGetFloat(CTMcontext aContext, CTMenum aProperty)
311 {
312 _CTMcontext * self = (_CTMcontext *) aContext;
313 if(!self) return 0.0f;
315 switch(aProperty)
316 {
317 case CTM_VERTEX_PRECISION:
318 return self->mVertexPrecision;
320 case CTM_NORMAL_PRECISION:
321 return self->mNormalPrecision;
323 default:
324 self->mError = CTM_INVALID_ARGUMENT;
325 }
327 return 0.0f;
328 }
330 //-----------------------------------------------------------------------------
331 // ctmGetIntegerArray()
332 //-----------------------------------------------------------------------------
333 CTMEXPORT const CTMuint * CTMCALL ctmGetIntegerArray(CTMcontext aContext,
334 CTMenum aProperty)
335 {
336 _CTMcontext * self = (_CTMcontext *) aContext;
337 if(!self) return (CTMuint *) 0;
339 switch(aProperty)
340 {
341 case CTM_INDICES:
342 return self->mIndices;
344 default:
345 self->mError = CTM_INVALID_ARGUMENT;
346 }
348 return (CTMuint *) 0;
349 }
351 //-----------------------------------------------------------------------------
352 // ctmGetFloatArray()
353 //-----------------------------------------------------------------------------
354 CTMEXPORT const CTMfloat * CTMCALL ctmGetFloatArray(CTMcontext aContext,
355 CTMenum aProperty)
356 {
357 _CTMcontext * self = (_CTMcontext *) aContext;
358 _CTMfloatmap * map;
359 CTMuint i;
360 if(!self) return (CTMfloat *) 0;
362 // Did the user request a UV map?
363 if((aProperty >= CTM_UV_MAP_1) &&
364 ((CTMuint)(aProperty - CTM_UV_MAP_1) < self->mUVMapCount))
365 {
366 map = self->mUVMaps;
367 i = CTM_UV_MAP_1;
368 while(map && (i != aProperty))
369 {
370 map = map->mNext;
371 ++ i;
372 }
373 if(!map)
374 {
375 self->mError = CTM_INTERNAL_ERROR;
376 return (CTMfloat *) 0;
377 }
378 return map->mValues;
379 }
381 // Did the user request an attribute map?
382 if((aProperty >= CTM_ATTRIB_MAP_1) &&
383 ((CTMuint)(aProperty - CTM_ATTRIB_MAP_1) < self->mAttribMapCount))
384 {
385 map = self->mAttribMaps;
386 i = CTM_ATTRIB_MAP_1;
387 while(map && (i != aProperty))
388 {
389 map = map->mNext;
390 ++ i;
391 }
392 if(!map)
393 {
394 self->mError = CTM_INTERNAL_ERROR;
395 return (CTMfloat *) 0;
396 }
397 return map->mValues;
398 }
400 switch(aProperty)
401 {
402 case CTM_VERTICES:
403 return self->mVertices;
405 case CTM_NORMALS:
406 return self->mNormals;
408 default:
409 self->mError = CTM_INVALID_ARGUMENT;
410 }
412 return (CTMfloat *) 0;
413 }
415 //-----------------------------------------------------------------------------
416 // ctmGetNamedUVMap()
417 //-----------------------------------------------------------------------------
418 CTMEXPORT CTMenum CTMCALL ctmGetNamedUVMap(CTMcontext aContext,
419 const char * aName)
420 {
421 _CTMcontext * self = (_CTMcontext *) aContext;
422 _CTMfloatmap * map;
423 CTMuint result;
424 if(!self) return CTM_NONE;
426 map = self->mUVMaps;
427 result = CTM_UV_MAP_1;
428 while(map && (strcmp(aName, map->mName) != 0))
429 {
430 map = map->mNext;
431 ++ result;
432 }
433 if(!map)
434 {
435 return CTM_NONE;
436 }
437 return result;
438 }
440 //-----------------------------------------------------------------------------
441 // ctmGetUVMapString()
442 //-----------------------------------------------------------------------------
443 CTMEXPORT const char * CTMCALL ctmGetUVMapString(CTMcontext aContext,
444 CTMenum aUVMap, CTMenum aProperty)
445 {
446 _CTMcontext * self = (_CTMcontext *) aContext;
447 _CTMfloatmap * map;
448 CTMuint i;
449 if(!self) return (const char *) 0;
451 // Find the indicated map
452 map = self->mUVMaps;
453 i = CTM_UV_MAP_1;
454 while(map && (i != aUVMap))
455 {
456 ++ i;
457 map = map->mNext;
458 }
459 if(!map)
460 {
461 self->mError = CTM_INVALID_ARGUMENT;
462 return (const char *) 0;
463 }
465 // Get the requested string
466 switch(aProperty)
467 {
468 case CTM_NAME:
469 return (const char *) map->mName;
471 case CTM_FILE_NAME:
472 return (const char *) map->mFileName;
474 default:
475 self->mError = CTM_INVALID_ARGUMENT;
476 }
478 return (const char *) 0;
479 }
481 //-----------------------------------------------------------------------------
482 // ctmGetUVMapFloat()
483 //-----------------------------------------------------------------------------
484 CTMEXPORT CTMfloat CTMCALL ctmGetUVMapFloat(CTMcontext aContext,
485 CTMenum aUVMap, CTMenum aProperty)
486 {
487 _CTMcontext * self = (_CTMcontext *) aContext;
488 _CTMfloatmap * map;
489 CTMuint i;
490 if(!self) return 0.0f;
492 // Find the indicated map
493 map = self->mUVMaps;
494 i = CTM_UV_MAP_1;
495 while(map && (i != aUVMap))
496 {
497 ++ i;
498 map = map->mNext;
499 }
500 if(!map)
501 {
502 self->mError = CTM_INVALID_ARGUMENT;
503 return 0.0f;
504 }
506 // Get the requested string
507 switch(aProperty)
508 {
509 case CTM_PRECISION:
510 return map->mPrecision;
512 default:
513 self->mError = CTM_INVALID_ARGUMENT;
514 }
516 return 0.0f;
517 }
519 //-----------------------------------------------------------------------------
520 // ctmGetAttribMapString()
521 //-----------------------------------------------------------------------------
522 CTMEXPORT const char * CTMCALL ctmGetAttribMapString(CTMcontext aContext,
523 CTMenum aAttribMap, CTMenum aProperty)
524 {
525 _CTMcontext * self = (_CTMcontext *) aContext;
526 _CTMfloatmap * map;
527 CTMuint i;
528 if(!self) return (const char *) 0;
530 // Find the indicated map
531 map = self->mAttribMaps;
532 i = CTM_ATTRIB_MAP_1;
533 while(map && (i != aAttribMap))
534 {
535 ++ i;
536 map = map->mNext;
537 }
538 if(!map)
539 {
540 self->mError = CTM_INVALID_ARGUMENT;
541 return (const char *) 0;
542 }
544 // Get the requested string
545 switch(aProperty)
546 {
547 case CTM_NAME:
548 return (const char *) map->mName;
550 default:
551 self->mError = CTM_INVALID_ARGUMENT;
552 }
554 return (const char *) 0;
555 }
557 //-----------------------------------------------------------------------------
558 // ctmGetAttribMapFloat()
559 //-----------------------------------------------------------------------------
560 CTMEXPORT CTMfloat CTMCALL ctmGetAttribMapFloat(CTMcontext aContext,
561 CTMenum aAttribMap, CTMenum aProperty)
562 {
563 _CTMcontext * self = (_CTMcontext *) aContext;
564 _CTMfloatmap * map;
565 CTMuint i;
566 if(!self) return 0.0f;
568 // Find the indicated map
569 map = self->mAttribMaps;
570 i = CTM_ATTRIB_MAP_1;
571 while(map && (i != aAttribMap))
572 {
573 ++ i;
574 map = map->mNext;
575 }
576 if(!map)
577 {
578 self->mError = CTM_INVALID_ARGUMENT;
579 return 0.0f;
580 }
582 // Get the requested string
583 switch(aProperty)
584 {
585 case CTM_PRECISION:
586 return map->mPrecision;
588 default:
589 self->mError = CTM_INVALID_ARGUMENT;
590 }
592 return 0.0f;
593 }
595 //-----------------------------------------------------------------------------
596 // ctmGetNamedAttribMap()
597 //-----------------------------------------------------------------------------
598 CTMEXPORT CTMenum CTMCALL ctmGetNamedAttribMap(CTMcontext aContext,
599 const char * aName)
600 {
601 _CTMcontext * self = (_CTMcontext *) aContext;
602 _CTMfloatmap * map;
603 CTMuint result;
604 if(!self) return CTM_NONE;
606 map = self->mAttribMaps;
607 result = CTM_ATTRIB_MAP_1;
608 while(map && (strcmp(aName, map->mName) != 0))
609 {
610 map = map->mNext;
611 ++ result;
612 }
613 if(!map)
614 {
615 return CTM_NONE;
616 }
617 return result;
618 }
620 //-----------------------------------------------------------------------------
621 // ctmGetString()
622 //-----------------------------------------------------------------------------
623 CTMEXPORT const char * CTMCALL ctmGetString(CTMcontext aContext,
624 CTMenum aProperty)
625 {
626 _CTMcontext * self = (_CTMcontext *) aContext;
627 if(!self) return 0;
629 switch(aProperty)
630 {
631 case CTM_FILE_COMMENT:
632 return (const char *) self->mFileComment;
634 default:
635 self->mError = CTM_INVALID_ARGUMENT;
636 }
638 return (const char *) 0;
639 }
641 //-----------------------------------------------------------------------------
642 // ctmCompressionMethod()
643 //-----------------------------------------------------------------------------
644 CTMEXPORT void CTMCALL ctmCompressionMethod(CTMcontext aContext,
645 CTMenum aMethod)
646 {
647 _CTMcontext * self = (_CTMcontext *) aContext;
648 if(!self) return;
650 // You are only allowed to change compression attributes in export mode
651 if(self->mMode != CTM_EXPORT)
652 {
653 self->mError = CTM_INVALID_OPERATION;
654 return;
655 }
657 // Check arguments
658 if((aMethod != CTM_METHOD_RAW) && (aMethod != CTM_METHOD_MG1) &&
659 (aMethod != CTM_METHOD_MG2))
660 {
661 self->mError = CTM_INVALID_ARGUMENT;
662 return;
663 }
665 // Set method
666 self->mMethod = aMethod;
667 }
669 //-----------------------------------------------------------------------------
670 // ctmCompressionLevel()
671 //-----------------------------------------------------------------------------
672 CTMEXPORT void CTMCALL ctmCompressionLevel(CTMcontext aContext,
673 CTMuint aLevel)
674 {
675 _CTMcontext * self = (_CTMcontext *) aContext;
676 if(!self) return;
678 // You are only allowed to change compression attributes in export mode
679 if(self->mMode != CTM_EXPORT)
680 {
681 self->mError = CTM_INVALID_OPERATION;
682 return;
683 }
685 // Check arguments
686 if(aLevel > 9)
687 {
688 self->mError = CTM_INVALID_ARGUMENT;
689 return;
690 }
692 // Set the compression level
693 self->mCompressionLevel = aLevel;
694 }
696 //-----------------------------------------------------------------------------
697 // ctmVertexPrecision()
698 //-----------------------------------------------------------------------------
699 CTMEXPORT void CTMCALL ctmVertexPrecision(CTMcontext aContext,
700 CTMfloat aPrecision)
701 {
702 _CTMcontext * self = (_CTMcontext *) aContext;
703 if(!self) return;
705 // You are only allowed to change compression attributes in export mode
706 if(self->mMode != CTM_EXPORT)
707 {
708 self->mError = CTM_INVALID_OPERATION;
709 return;
710 }
712 // Check arguments
713 if(aPrecision <= 0.0f)
714 {
715 self->mError = CTM_INVALID_ARGUMENT;
716 return;
717 }
719 // Set precision
720 self->mVertexPrecision = aPrecision;
721 }
723 //-----------------------------------------------------------------------------
724 // ctmVertexPrecisionRel()
725 //-----------------------------------------------------------------------------
726 CTMEXPORT void CTMCALL ctmVertexPrecisionRel(CTMcontext aContext,
727 CTMfloat aRelPrecision)
728 {
729 _CTMcontext * self = (_CTMcontext *) aContext;
730 CTMfloat avgEdgeLength, * p1, * p2;
731 CTMuint edgeCount, i, j;
732 if(!self) return;
734 // You are only allowed to change compression attributes in export mode
735 if(self->mMode != CTM_EXPORT)
736 {
737 self->mError = CTM_INVALID_OPERATION;
738 return;
739 }
741 // Check arguments
742 if(aRelPrecision <= 0.0f)
743 {
744 self->mError = CTM_INVALID_ARGUMENT;
745 return;
746 }
748 // Calculate the average edge length (Note: we actually sum up all the half-
749 // edges, so in a proper solid mesh all connected edges are counted twice)
750 avgEdgeLength = 0.0f;
751 edgeCount = 0;
752 for(i = 0; i < self->mTriangleCount; ++ i)
753 {
754 p1 = &self->mVertices[self->mIndices[i * 3 + 2] * 3];
755 for(j = 0; j < 3; ++ j)
756 {
757 p2 = &self->mVertices[self->mIndices[i * 3 + j] * 3];
758 avgEdgeLength += sqrtf((p2[0] - p1[0]) * (p2[0] - p1[0]) +
759 (p2[1] - p1[1]) * (p2[1] - p1[1]) +
760 (p2[2] - p1[2]) * (p2[2] - p1[2]));
761 p1 = p2;
762 ++ edgeCount;
763 }
764 }
765 if(edgeCount == 0)
766 {
767 self->mError = CTM_INVALID_MESH;
768 return;
769 }
770 avgEdgeLength /= (CTMfloat) edgeCount;
772 // Set precision
773 self->mVertexPrecision = aRelPrecision * avgEdgeLength;
774 }
776 //-----------------------------------------------------------------------------
777 // ctmNormalPrecision()
778 //-----------------------------------------------------------------------------
779 CTMEXPORT void CTMCALL ctmNormalPrecision(CTMcontext aContext,
780 CTMfloat aPrecision)
781 {
782 _CTMcontext * self = (_CTMcontext *) aContext;
783 if(!self) return;
785 // You are only allowed to change compression attributes in export mode
786 if(self->mMode != CTM_EXPORT)
787 {
788 self->mError = CTM_INVALID_OPERATION;
789 return;
790 }
792 // Check arguments
793 if(aPrecision <= 0.0f)
794 {
795 self->mError = CTM_INVALID_ARGUMENT;
796 return;
797 }
799 // Set precision
800 self->mNormalPrecision = aPrecision;
801 }
803 //-----------------------------------------------------------------------------
804 // ctmUVCoordPrecision()
805 //-----------------------------------------------------------------------------
806 CTMEXPORT void CTMCALL ctmUVCoordPrecision(CTMcontext aContext,
807 CTMenum aUVMap, CTMfloat aPrecision)
808 {
809 _CTMcontext * self = (_CTMcontext *) aContext;
810 _CTMfloatmap * map;
811 CTMuint i;
812 if(!self) return;
814 // You are only allowed to change compression attributes in export mode
815 if(self->mMode != CTM_EXPORT)
816 {
817 self->mError = CTM_INVALID_OPERATION;
818 return;
819 }
821 // Check arguments
822 if(aPrecision <= 0.0f)
823 {
824 self->mError = CTM_INVALID_ARGUMENT;
825 return;
826 }
828 // Find the indicated map
829 map = self->mUVMaps;
830 i = CTM_UV_MAP_1;
831 while(map && (i != aUVMap))
832 {
833 ++ i;
834 map = map->mNext;
835 }
836 if(!map)
837 {
838 self->mError = CTM_INVALID_ARGUMENT;
839 return;
840 }
842 // Update the precision
843 map->mPrecision = aPrecision;
844 }
846 //-----------------------------------------------------------------------------
847 // ctmAttribPrecision()
848 //-----------------------------------------------------------------------------
849 CTMEXPORT void CTMCALL ctmAttribPrecision(CTMcontext aContext,
850 CTMenum aAttribMap, CTMfloat aPrecision)
851 {
852 _CTMcontext * self = (_CTMcontext *) aContext;
853 _CTMfloatmap * map;
854 CTMuint i;
855 if(!self) return;
857 // You are only allowed to change compression attributes in export mode
858 if(self->mMode != CTM_EXPORT)
859 {
860 self->mError = CTM_INVALID_OPERATION;
861 return;
862 }
864 // Check arguments
865 if(aPrecision <= 0.0f)
866 {
867 self->mError = CTM_INVALID_ARGUMENT;
868 return;
869 }
871 // Find the indicated map
872 map = self->mAttribMaps;
873 i = CTM_ATTRIB_MAP_1;
874 while(map && (i != aAttribMap))
875 {
876 ++ i;
877 map = map->mNext;
878 }
879 if(!map)
880 {
881 self->mError = CTM_INVALID_ARGUMENT;
882 return;
883 }
885 // Update the precision
886 map->mPrecision = aPrecision;
887 }
889 //-----------------------------------------------------------------------------
890 // ctmFileComment()
891 //-----------------------------------------------------------------------------
892 CTMEXPORT void CTMCALL ctmFileComment(CTMcontext aContext,
893 const char * aFileComment)
894 {
895 _CTMcontext * self = (_CTMcontext *) aContext;
896 int len;
897 if(!self) return;
899 // You are only allowed to change file attributes in export mode
900 if(self->mMode != CTM_EXPORT)
901 {
902 self->mError = CTM_INVALID_OPERATION;
903 return;
904 }
906 // Free the old comment string, if necessary
907 if(self->mFileComment)
908 {
909 free(self->mFileComment);
910 self->mFileComment = (char *) 0;
911 }
913 // Get length of string (if empty, do nothing)
914 if(!aFileComment)
915 return;
916 len = strlen(aFileComment);
917 if(!len)
918 return;
920 // Copy the string
921 self->mFileComment = (char *) malloc(len + 1);
922 if(!self->mFileComment)
923 {
924 self->mError = CTM_OUT_OF_MEMORY;
925 return;
926 }
927 strcpy(self->mFileComment, aFileComment);
928 }
930 //-----------------------------------------------------------------------------
931 // ctmDefineMesh()
932 //-----------------------------------------------------------------------------
933 CTMEXPORT void CTMCALL ctmDefineMesh(CTMcontext aContext,
934 const CTMfloat * aVertices, CTMuint aVertexCount, const CTMuint * aIndices,
935 CTMuint aTriangleCount, const CTMfloat * aNormals)
936 {
937 _CTMcontext * self = (_CTMcontext *) aContext;
938 if(!self) return;
940 // You are only allowed to (re)define the mesh in export mode
941 if(self->mMode != CTM_EXPORT)
942 {
943 self->mError = CTM_INVALID_OPERATION;
944 return;
945 }
947 // Check arguments
948 if(!aVertices || !aIndices || !aVertexCount || !aTriangleCount)
949 {
950 self->mError = CTM_INVALID_ARGUMENT;
951 return;
952 }
954 // Clear the old mesh, if any
955 _ctmClearMesh(self);
957 // Set vertex array pointer
958 self->mVertices = (CTMfloat *) aVertices;
959 self->mVertexCount = aVertexCount;
961 // Set index array pointer
962 self->mIndices = (CTMuint *) aIndices;
963 self->mTriangleCount = aTriangleCount;
965 // Set normal array pointer
966 self->mNormals = (CTMfloat *) aNormals;
967 }
969 //-----------------------------------------------------------------------------
970 // _ctmAddFloatMap()
971 //-----------------------------------------------------------------------------
972 static _CTMfloatmap * _ctmAddFloatMap(_CTMcontext * self,
973 const CTMfloat * aValues, const char * aName, const char * aFileName,
974 _CTMfloatmap ** aList)
975 {
976 _CTMfloatmap * map;
977 CTMuint len;
979 // Allocate memory for a new map list item and append it to the list
980 if(!*aList)
981 {
982 *aList = (_CTMfloatmap *) malloc(sizeof(_CTMfloatmap));
983 map = *aList;
984 }
985 else
986 {
987 map = *aList;
988 while(map->mNext)
989 map = map->mNext;
990 map->mNext = (_CTMfloatmap *) malloc(sizeof(_CTMfloatmap));
991 map = map->mNext;
992 }
993 if(!map)
994 {
995 self->mError = CTM_OUT_OF_MEMORY;
996 return (_CTMfloatmap *) 0;
997 }
999 // Init the map item
1000 memset(map, 0, sizeof(_CTMfloatmap));
1001 map->mPrecision = 1.0f / 1024.0f;
1002 map->mValues = (CTMfloat *) aValues;
1004 // Set name of the map
1005 if(aName)
1007 // Get length of string (if empty, do nothing)
1008 len = strlen(aName);
1009 if(len)
1011 // Copy the string
1012 map->mName = (char *) malloc(len + 1);
1013 if(!map->mName)
1015 self->mError = CTM_OUT_OF_MEMORY;
1016 free(map);
1017 return (_CTMfloatmap *) 0;
1019 strcpy(map->mName, aName);
1023 // Set file name reference for the map
1024 if(aFileName)
1026 // Get length of string (if empty, do nothing)
1027 len = strlen(aFileName);
1028 if(len)
1030 // Copy the string
1031 map->mFileName = (char *) malloc(len + 1);
1032 if(!map->mFileName)
1034 self->mError = CTM_OUT_OF_MEMORY;
1035 if(map->mName)
1036 free(map->mName);
1037 free(map);
1038 return (_CTMfloatmap *) 0;
1040 strcpy(map->mFileName, aFileName);
1044 return map;
1047 //-----------------------------------------------------------------------------
1048 // ctmAddUVMap()
1049 //-----------------------------------------------------------------------------
1050 CTMEXPORT CTMenum CTMCALL ctmAddUVMap(CTMcontext aContext,
1051 const CTMfloat * aUVCoords, const char * aName, const char * aFileName)
1053 _CTMcontext * self = (_CTMcontext *) aContext;
1054 _CTMfloatmap * map;
1055 if(!self) return CTM_NONE;
1057 // Add a new UV map to the UV map list
1058 map = _ctmAddFloatMap(self, aUVCoords, aName, aFileName, &self->mUVMaps);
1059 if(!map)
1060 return CTM_NONE;
1061 else
1063 // The default UV coordinate precision is 2^-12
1064 map->mPrecision = 1.0f / 4096.0f;
1065 ++ self->mUVMapCount;
1066 return CTM_UV_MAP_1 + self->mUVMapCount - 1;
1070 //-----------------------------------------------------------------------------
1071 // ctmAddAttribMap()
1072 //-----------------------------------------------------------------------------
1073 CTMEXPORT CTMenum CTMCALL ctmAddAttribMap(CTMcontext aContext,
1074 const CTMfloat * aAttribValues, const char * aName)
1076 _CTMcontext * self = (_CTMcontext *) aContext;
1077 _CTMfloatmap * map;
1078 if(!self) return CTM_NONE;
1080 // Add a new attribute map to the attribute map list
1081 map = _ctmAddFloatMap(self, aAttribValues, aName, (const char *) 0,
1082 &self->mAttribMaps);
1083 if(!map)
1084 return CTM_NONE;
1085 else
1087 // The default vertex attribute precision is 2^-8
1088 map->mPrecision = 1.0f / 256.0f;
1089 ++ self->mAttribMapCount;
1090 return CTM_ATTRIB_MAP_1 + self->mAttribMapCount - 1;
1094 //-----------------------------------------------------------------------------
1095 // _ctmDefaultRead()
1096 //-----------------------------------------------------------------------------
1097 static CTMuint CTMCALL _ctmDefaultRead(void * aBuf, CTMuint aCount,
1098 void * aUserData)
1100 return (CTMuint) fread(aBuf, 1, (size_t) aCount, (FILE *) aUserData);
1103 //-----------------------------------------------------------------------------
1104 // ctmLoad()
1105 //-----------------------------------------------------------------------------
1106 CTMEXPORT void CTMCALL ctmLoad(CTMcontext aContext, const char * aFileName)
1108 _CTMcontext * self = (_CTMcontext *) aContext;
1109 FILE * f;
1110 if(!self) return;
1112 // You are only allowed to load data in import mode
1113 if(self->mMode != CTM_IMPORT)
1115 self->mError = CTM_INVALID_OPERATION;
1116 return;
1119 // Open file stream
1120 f = fopen(aFileName, "rb");
1121 if(!f)
1123 self->mError = CTM_FILE_ERROR;
1124 return;
1127 // Load the file
1128 ctmLoadCustom(self, _ctmDefaultRead, (void *) f);
1130 // Close file stream
1131 fclose(f);
1134 //-----------------------------------------------------------------------------
1135 // _ctmAllocateFloatMaps()
1136 //-----------------------------------------------------------------------------
1137 static CTMuint _ctmAllocateFloatMaps(_CTMcontext * self,
1138 _CTMfloatmap ** aMapListPtr, CTMuint aCount, CTMuint aChannels)
1140 _CTMfloatmap ** mapListPtr;
1141 CTMuint i, size;
1143 mapListPtr = aMapListPtr;
1144 for(i = 0; i < aCount; ++ i)
1146 // Allocate & clear memory for this map
1147 *mapListPtr = (_CTMfloatmap *) malloc(sizeof(_CTMfloatmap));
1148 if(!*mapListPtr)
1150 self->mError = CTM_OUT_OF_MEMORY;
1151 return CTM_FALSE;
1153 memset(*mapListPtr, 0, sizeof(_CTMfloatmap));
1155 // Allocate & clear memory for the float array
1156 size = aChannels * sizeof(CTMfloat) * self->mVertexCount;
1157 (*mapListPtr)->mValues = (CTMfloat *) malloc(size);
1158 if(!(*mapListPtr)->mValues)
1160 self->mError = CTM_OUT_OF_MEMORY;
1161 return CTM_FALSE;
1163 memset((*mapListPtr)->mValues, 0, size);
1165 // Next map...
1166 mapListPtr = &(*mapListPtr)->mNext;
1169 return CTM_TRUE;
1172 //-----------------------------------------------------------------------------
1173 // ctmLoadCustom()
1174 //-----------------------------------------------------------------------------
1175 CTMEXPORT void CTMCALL ctmLoadCustom(CTMcontext aContext, CTMreadfn aReadFn,
1176 void * aUserData)
1178 _CTMcontext * self = (_CTMcontext *) aContext;
1179 CTMuint formatVersion, flags, method;
1180 if(!self) return;
1182 // You are only allowed to load data in import mode
1183 if(self->mMode != CTM_IMPORT)
1185 self->mError = CTM_INVALID_OPERATION;
1186 return;
1189 // Initialize stream
1190 self->mReadFn = aReadFn;
1191 self->mUserData = aUserData;
1193 // Clear any old mesh arrays
1194 _ctmClearMesh(self);
1196 // Read header from stream
1197 if(_ctmStreamReadUINT(self) != FOURCC("OCTM"))
1199 self->mError = CTM_BAD_FORMAT;
1200 return;
1202 formatVersion = _ctmStreamReadUINT(self);
1203 if(formatVersion != _CTM_FORMAT_VERSION)
1205 self->mError = CTM_UNSUPPORTED_FORMAT_VERSION;
1206 return;
1208 method = _ctmStreamReadUINT(self);
1209 if(method == FOURCC("RAW\0"))
1210 self->mMethod = CTM_METHOD_RAW;
1211 else if(method == FOURCC("MG1\0"))
1212 self->mMethod = CTM_METHOD_MG1;
1213 else if(method == FOURCC("MG2\0"))
1214 self->mMethod = CTM_METHOD_MG2;
1215 else
1217 self->mError = CTM_BAD_FORMAT;
1218 return;
1220 self->mVertexCount = _ctmStreamReadUINT(self);
1221 if(self->mVertexCount == 0)
1223 self->mError = CTM_BAD_FORMAT;
1224 return;
1226 self->mTriangleCount = _ctmStreamReadUINT(self);
1227 if(self->mTriangleCount == 0)
1229 self->mError = CTM_BAD_FORMAT;
1230 return;
1232 self->mUVMapCount = _ctmStreamReadUINT(self);
1233 self->mAttribMapCount = _ctmStreamReadUINT(self);
1234 flags = _ctmStreamReadUINT(self);
1235 _ctmStreamReadSTRING(self, &self->mFileComment);
1237 // Allocate memory for the mesh arrays
1238 self->mVertices = (CTMfloat *) malloc(self->mVertexCount * sizeof(CTMfloat) * 3);
1239 if(!self->mVertices)
1241 self->mError = CTM_OUT_OF_MEMORY;
1242 return;
1244 self->mIndices = (CTMuint *) malloc(self->mTriangleCount * sizeof(CTMuint) * 3);
1245 if(!self->mIndices)
1247 _ctmClearMesh(self);
1248 self->mError = CTM_OUT_OF_MEMORY;
1249 return;
1251 if(flags & _CTM_HAS_NORMALS_BIT)
1253 self->mNormals = (CTMfloat *) malloc(self->mVertexCount * sizeof(CTMfloat) * 3);
1254 if(!self->mNormals)
1256 _ctmClearMesh(self);
1257 self->mError = CTM_OUT_OF_MEMORY;
1258 return;
1262 // Allocate memory for the UV and attribute maps (if any)
1263 if(!_ctmAllocateFloatMaps(self, &self->mUVMaps, self->mUVMapCount, 2))
1265 _ctmClearMesh(self);
1266 self->mError = CTM_OUT_OF_MEMORY;
1267 return;
1269 if(!_ctmAllocateFloatMaps(self, &self->mAttribMaps, self->mAttribMapCount, 4))
1271 _ctmClearMesh(self);
1272 self->mError = CTM_OUT_OF_MEMORY;
1273 return;
1276 // Uncompress from stream
1277 switch(self->mMethod)
1279 case CTM_METHOD_RAW:
1280 _ctmUncompressMesh_RAW(self);
1281 break;
1283 case CTM_METHOD_MG1:
1284 _ctmUncompressMesh_MG1(self);
1285 break;
1287 case CTM_METHOD_MG2:
1288 _ctmUncompressMesh_MG2(self);
1289 break;
1291 default:
1292 self->mError = CTM_INTERNAL_ERROR;
1295 // Check mesh integrity
1296 if(!_ctmCheckMeshIntegrity(self))
1298 self->mError = CTM_INVALID_MESH;
1299 return;
1303 //-----------------------------------------------------------------------------
1304 // _ctmDefaultWrite()
1305 //-----------------------------------------------------------------------------
1306 static CTMuint CTMCALL _ctmDefaultWrite(const void * aBuf, CTMuint aCount,
1307 void * aUserData)
1309 return (CTMuint) fwrite(aBuf, 1, (size_t) aCount, (FILE *) aUserData);
1312 //-----------------------------------------------------------------------------
1313 // ctmSave()
1314 //-----------------------------------------------------------------------------
1315 CTMEXPORT void CTMCALL ctmSave(CTMcontext aContext, const char * aFileName)
1317 _CTMcontext * self = (_CTMcontext *) aContext;
1318 FILE * f;
1319 if(!self) return;
1321 // You are only allowed to save data in export mode
1322 if(self->mMode != CTM_EXPORT)
1324 self->mError = CTM_INVALID_OPERATION;
1325 return;
1328 // Open file stream
1329 f = fopen(aFileName, "wb");
1330 if(!f)
1332 self->mError = CTM_FILE_ERROR;
1333 return;
1336 // Save the file
1337 ctmSaveCustom(self, _ctmDefaultWrite, (void *) f);
1339 // Close file stream
1340 fclose(f);
1343 //-----------------------------------------------------------------------------
1344 // ctmSaveCustom()
1345 //-----------------------------------------------------------------------------
1346 void CTMCALL ctmSaveCustom(CTMcontext aContext, CTMwritefn aWriteFn,
1347 void * aUserData)
1349 _CTMcontext * self = (_CTMcontext *) aContext;
1350 CTMuint flags;
1351 if(!self) return;
1353 // You are only allowed to save data in export mode
1354 if(self->mMode != CTM_EXPORT)
1356 self->mError = CTM_INVALID_OPERATION;
1357 return;
1360 // Check mesh integrity
1361 if(!_ctmCheckMeshIntegrity(self))
1363 self->mError = CTM_INVALID_MESH;
1364 return;
1367 // Initialize stream
1368 self->mWriteFn = aWriteFn;
1369 self->mUserData = aUserData;
1371 // Determine flags
1372 flags = 0;
1373 if(self->mNormals)
1374 flags |= _CTM_HAS_NORMALS_BIT;
1376 // Write header to stream
1377 _ctmStreamWrite(self, (void *) "OCTM", 4);
1378 _ctmStreamWriteUINT(self, _CTM_FORMAT_VERSION);
1379 switch(self->mMethod)
1381 case CTM_METHOD_RAW:
1382 _ctmStreamWrite(self, (void *) "RAW\0", 4);
1383 break;
1385 case CTM_METHOD_MG1:
1386 _ctmStreamWrite(self, (void *) "MG1\0", 4);
1387 break;
1389 case CTM_METHOD_MG2:
1390 _ctmStreamWrite(self, (void *) "MG2\0", 4);
1391 break;
1393 default:
1394 self->mError = CTM_INTERNAL_ERROR;
1395 return;
1397 _ctmStreamWriteUINT(self, self->mVertexCount);
1398 _ctmStreamWriteUINT(self, self->mTriangleCount);
1399 _ctmStreamWriteUINT(self, self->mUVMapCount);
1400 _ctmStreamWriteUINT(self, self->mAttribMapCount);
1401 _ctmStreamWriteUINT(self, flags);
1402 _ctmStreamWriteSTRING(self, self->mFileComment);
1404 // Compress to stream
1405 switch(self->mMethod)
1407 case CTM_METHOD_RAW:
1408 _ctmCompressMesh_RAW(self);
1409 break;
1411 case CTM_METHOD_MG1:
1412 _ctmCompressMesh_MG1(self);
1413 break;
1415 case CTM_METHOD_MG2:
1416 _ctmCompressMesh_MG2(self);
1417 break;
1419 default:
1420 self->mError = CTM_INTERNAL_ERROR;
1421 return;