Oolite 1.91.0.7604-240417-a536cbe
Loading...
Searching...
No Matches
OOMesh.m
Go to the documentation of this file.
1/*
2
3OOMesh.m
4
5A note on memory management:
6The dynamically-sized buffers used by OOMesh (_vertex etc) are the byte arrays
7of NSDatas, which are tracked using the _retainedObjects dictionary. This
8simplifies the implementation of -dealloc, but more importantly, it means
9bytes are refcounted. This means bytes read from the cache don't need to be
10copied, we just need to retain the relevant NSData object (by sticking it in
11_retainedObjects).
12
13Since _retainedObjects is a dictionary its members can be replaced,
14potentially allowing mutable meshes, although we have no use for this at
15present.
16
17
18Oolite
19Copyright (C) 2004-2013 Giles C Williams and contributors
20
21This program is free software; you can redistribute it and/or
22modify it under the terms of the GNU General Public License
23as published by the Free Software Foundation; either version 2
24of the License, or (at your option) any later version.
25
26This program is distributed in the hope that it will be useful,
27but WITHOUT ANY WARRANTY; without even the implied warranty of
28MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29GNU General Public License for more details.
30
31You should have received a copy of the GNU General Public License
32along with this program; if not, write to the Free Software
33Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
34MA 02110-1301, USA.
35
36*/
37
38#import "OOMesh.h"
39#import "Universe.h"
41#import "ResourceManager.h"
42#import "Entity.h" // for NO_DRAW_DISTANCE_FACTOR.
43#import "Octree.h"
45#import "OOBasicMaterial.h"
49#import "OODebugGLDrawing.h"
50#import "OOShaderMaterial.h"
51#import "OOMacroOpenGL.h"
53#import "OODebugFlags.h"
55
57#import "OODebugStandards.h"
58
59// If set, collision octree depth varies depending on the size of the mesh.
60#define ADAPTIVE_OCTREE_DEPTH 1
61
62// If set, cachable memory is scribbled with FEEDFACE to identify junk in cache.
63#define SCRIBBLE 0
64
65
66enum
67{
68 kBaseOctreeDepth = 5, // 32x32x32
69// kMaxOctreeDepth declared in Octree.h.
70 kSmallOctreeDepth = 4, // 16x16x16
71 kOctreeSizeThreshold = 900, // Size at which we start increasing octree depth
73};
74
75
82
83
84static NSString * const kOOLogMeshDataNotFound = @"mesh.load.failed.fileNotFound";
85static NSString * const kOOLogMeshTooManyMaterials = @"mesh.load.failed.tooManyMaterials";
86
87
88#if OOMESH_PROFILE
89#define PROFILE(tag) do { _stopwatchLastTime = Profile(tag, _stopwatch, _stopwatchLastTime); } while (0)
90static OOTimeDelta Profile(NSString *tag, OOProfilingStopwatch *stopwatch, OOTimeDelta lastTime)
91{
92 OOTimeDelta now = [stopwatch currentTime];
93 OOLog(@"mesh.profile", @"Mesh profile: stage %@, %g seconds (delta %g)", tag, now, now - lastTime);
94 return now;
95}
96#else
97#define PROFILE(tag) do {} while (0)
98#endif
99
100
101/* VertexFaceRef
102 List of indices of faces used by a given vertex.
103 Always access using the provided functions.
104
105 NOTE: VFRAddFace may use autoreleased memory. All accesses to a given VFR
106 must be inside the same autorelease pool.
107*/
108enum
109{
110#if OOLITE_64_BIT
111 kVertexFaceDefInternalCount = 11 // sizeof (VertexFaceRef) = 32
112#else
113 kVertexFaceDefInternalCount = 5 // sizeof (VertexFaceRef) = 16
114#endif
116
117typedef struct VertexFaceRef
118{
119 uint16_t internCount;
120 uint16_t internFaces[kVertexFaceDefInternalCount];
121 NSMutableArray *extra;
123
124
125static void VFRAddFace(VertexFaceRef *vfr, NSUInteger index);
126static NSUInteger VFRGetCount(VertexFaceRef *vfr);
127static NSUInteger VFRGetFaceAtIndex(VertexFaceRef *vfr, NSUInteger index);
128
129
130@interface OOMesh (Private) <NSMutableCopying, OOGraphicsResetClient>
131
132- (id)initWithName:(NSString *)name
133 cacheKey:(NSString *)cacheKey
134materialDictionary:(NSDictionary *)materialDict
135 shadersDictionary:(NSDictionary *)shadersDict
136 smooth:(BOOL)smooth
137 shaderMacros:(NSDictionary *)macros
138shaderBindingTarget:(id<OOWeakReferenceSupport>)object
139 scaleFactor:(float)scale
140 cacheWriteable:(BOOL)cacheWriteable;
141
142- (BOOL) loadData:(NSString *)filename scaleFactor:(float)scale;
144- (void) generateFaceTangents;
145- (void) calculateVertexNormalsAndTangentsWithFaceRefs:(VertexFaceRef *)faceRefs;
146- (void) calculateVertexTangentsWithFaceRefs:(VertexFaceRef *)faceRefs;
147
148- (void) deleteDisplayLists;
149
150- (NSDictionary*) modelData;
151- (BOOL) setModelFromModelData:(NSDictionary*) dict name:(NSString *)fileName;
152
153- (void) getNormal:(Vector *)outNormal andTangent:(Vector *)outTangent forVertex:(OOMeshVertexCount)v_index inSmoothGroup:(OOMeshSmoothGroup)smoothGroup;
154
155- (BOOL) setUpVertexArrays;
156
158
159- (void) rescaleByFactor:(GLfloat)factor;
160
161#ifndef NDEBUG
162- (void)debugDrawNormals;
163#endif
164
165// Manage set of objects we need to hang on to, particularly NSDatas owning buffers.
166- (void) setRetainedObject:(id)object forKey:(NSString *)key;
167- (void *) allocateBytesWithSize:(size_t)size count:(NSUInteger)count key:(NSString *)key;
168
169// Allocate all per-vertex/per-face buffers.
170- (BOOL) allocateVertexBuffersWithCount:(NSUInteger)count;
171- (BOOL) allocateNormalBuffersWithCount:(NSUInteger)count;
172- (BOOL) allocateFaceBuffersWithCount:(NSUInteger)count;
173- (BOOL) allocateVertexArrayBuffersWithCount:(NSUInteger)count;
174
175- (void) renameTexturesFrom:(NSString *)from to:(NSString *)to;
176
177@end
178
179
180@interface OOCacheManager (OOMesh)
181
182+ (NSDictionary *)meshDataForName:(NSString *)inShipName;
183+ (void)setMeshData:(NSDictionary *)inData forName:(NSString *)inShipName;
184
185@end
186
187
189{
190 /* True for modes that predate the "normal mode" concept, i.e. per-face
191 and smooth. These modes require automatic winding correction.
192 */
193 switch (mode)
194 {
197 return YES;
198
200 return NO;
201 }
202
203#ifndef NDEBUG
204 [NSException raise:NSInvalidArgumentException format:@"Unexpected normal mode in %s", __PRETTY_FUNCTION__];
205#endif
206 return NO;
207}
208
209
211{
212 /* True for modes that have per-vertex normals, i.e. not per-face mode.
213 */
214 switch (mode)
215 {
217 return NO;
218
221 return YES;
222 }
223
224#ifndef NDEBUG
225 [NSException raise:NSInvalidArgumentException format:@"Unexpected normal mode in %s", __PRETTY_FUNCTION__];
226#endif
227 return NO;
228}
229
230
231@implementation OOMesh
232
233+ (instancetype) meshWithName:(NSString *)name
234 cacheKey:(NSString *)cacheKey
235 materialDictionary:(NSDictionary *)materialDict
236 shadersDictionary:(NSDictionary *)shadersDict
237 smooth:(BOOL)smooth
238 shaderMacros:(NSDictionary *)macros
239 shaderBindingTarget:(id<OOWeakReferenceSupport>)object
240{
241 return [[[self alloc] initWithName:name
242 cacheKey:cacheKey
243 materialDictionary:materialDict
244 shadersDictionary:shadersDict
245 smooth:smooth
246 shaderMacros:macros
247 shaderBindingTarget:object
248 scaleFactor:1.0f
249 cacheWriteable:YES] autorelease];
250}
251
252+ (instancetype) meshWithName:(NSString *)name
253 cacheKey:(NSString *)cacheKey
254 materialDictionary:(NSDictionary *)materialDict
255 shadersDictionary:(NSDictionary *)shadersDict
256 smooth:(BOOL)smooth
257 shaderMacros:(NSDictionary *)macros
258 shaderBindingTarget:(id<OOWeakReferenceSupport>)object
259 scaleFactor:(float)scale
260 cacheWriteable:(BOOL)cacheWriteable
261{
262 return [[[self alloc] initWithName:name
263 cacheKey:cacheKey
264 materialDictionary:materialDict
265 shadersDictionary:shadersDict
266 smooth:smooth
267 shaderMacros:macros
268 shaderBindingTarget:object
269 scaleFactor:scale
270 cacheWriteable:cacheWriteable] autorelease];
271}
272
273
274+ (OOMaterial *)placeholderMaterial
275{
276 static OOBasicMaterial *placeholderMaterial = nil;
277
278 if (placeholderMaterial == nil)
279 {
280 placeholderMaterial = [[OOBasicMaterial alloc] initWithName:@"/placeholder/" configuration:[[ResourceManager materialDefaults] oo_dictionaryForKey:@"no-textures-material"]];
281 }
282
283 return placeholderMaterial;
284}
285
286
287- (id)init
288{
289 self = [super init];
290 if (self == nil) return nil;
291
292 baseFile = @"No Model";
293 baseFileOctreeCacheRef = @"No Model-0.000";
294 _cacheWriteable = YES;
295#if OO_MULTITEXTURE
296 _textureUnitCount = NSNotFound;
297#endif
298
299 _lastPosition = kZeroVector;
300 _lastRotMatrix = kZeroMatrix; // not identity
301 _lastBoundingBox = kZeroBoundingBox;
302
303 return self;
304}
305
306
307- (void) dealloc
308{
309 unsigned i;
310
311 DESTROY(baseFileOctreeCacheRef);
312 DESTROY(baseFile);
313 DESTROY(octree);
314
315 [self deleteDisplayLists];
316
317 for (i = 0; i != kOOMeshMaxMaterials; ++i)
318 {
319 DESTROY(materials[i]);
320 DESTROY(materialKeys[i]);
321 }
322
324
325 DESTROY(_retainedObjects);
326
327 DESTROY(_materialDict);
328 DESTROY(_shadersDict);
329 DESTROY(_cacheKey);
330 DESTROY(_shaderMacros);
331 DESTROY(_shaderBindingTarget);
332
333#if OOMESH_PROFILE
334 DESTROY(_stopwatch);
335#endif
336
337 [super dealloc];
338}
339
340
341static NSString *NormalModeDescription(OOMeshNormalMode mode)
342{
343 switch (mode)
344 {
345 case kNormalModePerFace: return @"per-face";
346 case kNormalModeSmooth: return @"smooth";
347 case kNormalModeExplicit: return @"explicit";
348 }
349
350 return @"unknown";
351}
352
353
354- (NSString *)descriptionComponents
355{
356 return [NSString stringWithFormat:@"\"%@\", %zu vertices, %zu faces, radius: %g m normals: %@", [self modelName], [self vertexCount], [self faceCount], [self collisionRadius], NormalModeDescription(_normalMode)];
357}
358
359
360- (id)copyWithZone:(NSZone *)zone
361{
362 if (zone == [self zone]) return [self retain]; // OK because we're immutable seen from the outside
363 else return [self mutableCopyWithZone:zone];
364}
365
366
367- (NSString *) modelName
368{
369 return baseFile;
370}
371
372
373- (size_t)vertexCount
374{
375 return vertexCount;
376}
377
378
379- (size_t)faceCount
380{
381 return faceCount;
382}
383
384
385- (void)renderOpaqueParts
386{
388
389 BOOL meshBelongsToVisualEffect = [_shaderBindingTarget isVisualEffect];
390
392
393 OOGL(glVertexPointer(3, GL_FLOAT, 0, _displayLists.vertexArray));
394 OOGL(glNormalPointer(GL_FLOAT, 0, _displayLists.normalArray));
395
396 // for visual effects enable blending. This will allow use of alpha
397 // channel in shaders - note, this is a bit of cheating the system,
398 // which expects blending to be disabled at this point
399 if (meshBelongsToVisualEffect)
400 {
401 OOGL(glEnable(GL_BLEND));
402 OOGL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
403 }
404
405#if OO_SHADERS
406 if ([[OOOpenGLExtensionManager sharedManager] shadersSupported])
407 {
408 OOGL(glEnableVertexAttribArrayARB(kTangentAttributeIndex));
409 OOGL(glVertexAttribPointerARB(kTangentAttributeIndex, 3, GL_FLOAT, GL_FALSE, 0, _displayLists.tangentArray));
410 }
411#endif
412
413 BOOL usingNormalsAsTexCoords = NO;
415
416 /* FIXME: really, really horrible hack to set up texture coordinates for
417 each texture unit. Very messy and still fails to handle some possibly-
418 basic stuff, like switching usingNormalsAsTexCoords per texture unit.
419 The right way to do this is probably to move attribute setup into the
420 material model.
421 -- Ahruman 2010-04-12
422 */
423#if OO_MULTITEXTURE
424 if (_textureUnitCount == NSNotFound)
425 {
426 _textureUnitCount = 0;
427 for (ti = 0; ti < materialCount; ti++)
428 {
429 NSUInteger count = [materials[ti] countOfTextureUnitsWithBaseCoordinates];
430 if (_textureUnitCount < count) _textureUnitCount = count;
431 }
432 }
433
434 NSUInteger unit;
435 if (_textureUnitCount <= 1)
436 {
437 OOGL(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
438 }
439 else
440 {
441 /* It should not be possible to have multiple texture units if
442 texture combiners are not available.
443 */
444 NSAssert2([[OOOpenGLExtensionManager sharedManager] textureCombinersSupported], @"Mesh %@ uses %lu texture units, but multitexturing is not available.", [self shortDescription], _textureUnitCount);
445
446 for (unit = 0; unit < _textureUnitCount; unit++)
447 {
448 OOGL(glClientActiveTextureARB(GL_TEXTURE0_ARB + unit));
449 OOGL(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
450 }
451 }
452#else
453 OOGL(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
454#endif
455
456 @try
457 {
458 if (!listsReady)
459 {
460 OOGL(displayList0 = glGenLists(materialCount));
461
462 // Ensure all textures are loaded
463 for (ti = 0; ti < materialCount; ti++)
464 {
465 [materials[ti] ensureFinishedLoading];
466 }
467 }
468
469 for (ti = 0; ti < materialCount; ti++)
470 {
471 BOOL wantsNormalsAsTextureCoordinates = [materials[ti] wantsNormalsAsTextureCoordinates];
472 if (ti == 0 || wantsNormalsAsTextureCoordinates != usingNormalsAsTexCoords)
473 {
474 // FIXME: enabling/disabling texturing should be handled by the material.
475#if OO_MULTITEXTURE
476 for (unit = 0; unit < _textureUnitCount; unit++)
477 {
478 if (_textureUnitCount > 1)
479 {
480 OOGL(glClientActiveTextureARB(GL_TEXTURE0_ARB + unit));
481 OOGL(glActiveTextureARB(GL_TEXTURE0_ARB + unit));
482 }
483#endif
484 if (!wantsNormalsAsTextureCoordinates)
485 {
486 OOGL(glDisable(GL_TEXTURE_CUBE_MAP));
487 OOGL(glTexCoordPointer(2, GL_FLOAT, 0, _displayLists.textureUVArray));
488 /* FIXME: Not including the line below breaks multitexturing in no-shaders mode.
489 However, the OpenGL state manager should probably be handling this;
490 TEXTURE_2D is part of OPENGL_STATE_OPAQUE, which has already been set.
491 - Nikos 20130103
492 */
493 OOGL(glEnable(GL_TEXTURE_2D));
494 }
495 else
496 {
497 OOGL(glDisable(GL_TEXTURE_2D));
498 OOGL(glTexCoordPointer(3, GL_FLOAT, 0, _displayLists.vertexArray));
499 OOGL(glEnable(GL_TEXTURE_CUBE_MAP));
500 }
501#if OO_MULTITEXTURE
502 }
503#endif
504 usingNormalsAsTexCoords = wantsNormalsAsTextureCoordinates;
505 }
506
507 [materials[ti] apply];
508 OOGL(glDrawArrays(GL_TRIANGLES, triangle_range[ti].location, triangle_range[ti].length));
509 }
510
511 listsReady = YES;
512 brokenInRender = NO;
513 }
514 @catch (NSException *exception)
515 {
516 if (!brokenInRender)
517 {
518 OOLog(kOOLogException, @"***** %s for %@ encountered exception: %@ : %@ *****", __PRETTY_FUNCTION__, self, [exception name], [exception reason]);
519 brokenInRender = YES;
520 }
521 if ([[exception name] hasPrefix:@"Oolite"]) [UNIVERSE handleOoliteException:exception]; // handle these ourself
522 else @throw exception; // pass these on
523 }
524
525#if OO_SHADERS
526 if ([[OOOpenGLExtensionManager sharedManager] shadersSupported])
527 {
528 OOGL(glDisableVertexAttribArrayARB(kTangentAttributeIndex));
529 }
530#endif
531
533 OOCheckOpenGLErrors(@"OOMesh after drawing %@", self);
534
535#if OO_MULTITEXTURE
536 if (_textureUnitCount <= 1)
537 {
538 OOGL(glDisableClientState(GL_TEXTURE_COORD_ARRAY));
539 }
540 else
541 {
542 for (unit = 0; unit < _textureUnitCount; unit++)
543 {
544 OOGL(glClientActiveTextureARB(GL_TEXTURE0_ARB + unit));
545 OOGL(glDisableClientState(GL_TEXTURE_COORD_ARRAY));
546 }
547
548 OOGL(glClientActiveTextureARB(GL_TEXTURE0_ARB));
549 OOGL(glActiveTextureARB(GL_TEXTURE0_ARB));
550 }
551#else
552 OOGL(glDisableClientState(GL_TEXTURE_COORD_ARRAY));
553#endif
554
555#ifndef NDEBUG
556 if (gDebugFlags & DEBUG_DRAW_NORMALS) [self debugDrawNormals];
557 if (gDebugFlags & DEBUG_OCTREE_DRAW) [[self octree] drawOctree];
558#endif
559
560 // visual effect - disable previously enabled blending
561 if (meshBelongsToVisualEffect) OOGL(glDisable(GL_BLEND));
562
564}
565
566
567- (void) rebindMaterials
568{
570 OOMaterial *material = nil;
571
572 if (materialCount != 0)
573 {
574 for (i = 0; i != materialCount; ++i)
575 {
576 OOMaterial *oldMaterial = materials[i];
577
578 if (![materialKeys[i] isEqualToString:@"_oo_placeholder_material"])
579 {
580 material = [OOMaterial materialWithName:materialKeys[i]
581 cacheKey:_cacheKey
582 materialDictionary:_materialDict
583 shadersDictionary:_shadersDict
584 macros:_shaderMacros
585 bindingTarget:[_shaderBindingTarget weakRefUnderlyingObject] // Windows DEP fix.
586 forSmoothedMesh:IsPerVertexNormalMode(_normalMode)];
587 }
588 else
589 {
590 material = nil;
591 }
592
593 if (material != nil)
594 {
595 materials[i] = [material retain];
596 }
597 else
598 {
599 materials[i] = [[OOMesh placeholderMaterial] retain];
600 }
601
602 /* Release is deferred to here to ensure we don't end up releasing
603 a texture that's not in the recent-cache and then reloading it.
604 */
605 [oldMaterial release];
606 }
607 }
608}
609
610
611- (NSDictionary *) materials
612{
613 return _materialDict;
614}
615
616
617- (NSDictionary *) shaders
618{
619 return _shadersDict;
620}
621
622
623- (BOOL)hasOpaqueParts
624{
625 return YES;
626}
627
628- (GLfloat)collisionRadius
629{
630 return collisionRadius;
631}
632
633
634- (GLfloat)maxDrawDistance
635{
636 return maxDrawDistance;
637}
638
639
640#if ADAPTIVE_OCTREE_DEPTH
641- (unsigned) octreeDepth
642{
643 float threshold = kOctreeSizeThreshold;
644 unsigned result = kBaseOctreeDepth;
645 GLfloat xs, ys, zs, t, size;
646
647 bounding_box_get_dimensions(boundingBox, &xs, &ys, &zs);
648 // Shuffle dimensions around so zs is smallest
649 if (xs < zs) { t = zs; zs = xs; xs = t; }
650 if (ys < zs) { t = zs; zs = ys; ys = t; }
651 size = (xs + ys) / 2.0f; // Use average of two largest
652
654 else while (result < kMaxOctreeDepth)
655 {
656 if (size < threshold) break;
657 threshold *= 2.0f;
658 result++;
659 }
660
661 OOLog(@"mesh.load.octree.size", @"Selected octree depth %u for size %g for %@", result, size, baseFile);
662 return result;
663}
664#else
665- (unsigned) octreeDepth
666{
667 return kBaseOctreeDepth;
668}
669#endif
670
671
672- (Octree *)octree
673{
674 if (octree == nil)
675 {
676 octree = [[OOCacheManager octreeForModel:baseFileOctreeCacheRef] retain];
677 if (octree == nil)
678 {
679 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
680
683 for (i = 0; i < faceCount; i++)
684 {
685 // Somewhat surprisingly, this method doesn't even show up in profiles. -- Ahruman 2012-09-22
686 Triangle tri;
687 tri.v[0] = _vertices[_faces[i].vertex[0]];
688 tri.v[1] = _vertices[_faces[i].vertex[1]];
689 tri.v[2] = _vertices[_faces[i].vertex[2]];
690 [converter addTriangle:tri];
691 }
692
693 octree = [converter findOctreeToDepth:[self octreeDepth]];
694 [octree retain];
695 if (EXPECT(_cacheWriteable))
696 {
697 [OOCacheManager setOctree:octree forModel:baseFileOctreeCacheRef];
698 }
699
700 [pool release];
701 }
702 else
703 {
704 OOLog(@"mesh.load.octreeCached", @"Retrieved octree \"%@\" from cache.", baseFileOctreeCacheRef);
705 }
706 }
707
708 return octree;
709}
710
711
712- (BoundingBox) findBoundingBoxRelativeToPosition:(Vector)opv
713 basis:(Vector)ri :(Vector)rj :(Vector)rk
714 selfPosition:(Vector)position
715 selfBasis:(Vector)si :(Vector)sj :(Vector)sk
716{
717 BoundingBox result;
718 Vector pv, rv;
719
720 // FIXME: rewrite with matrices
721 Vector rpos = vector_subtract(position, opv); // model origin relative to opv
722
723 rv.x = dot_product(ri,rpos);
724 rv.y = dot_product(rj,rpos);
725 rv.z = dot_product(rk,rpos); // model origin rel to opv in ijk
726
727 if (EXPECT_NOT(vertexCount < 1))
728 {
729 bounding_box_reset_to_vector(&result, rv);
730 }
731 else
732 {
733 pv.x = rpos.x + si.x * _vertices[0].x + sj.x * _vertices[0].y + sk.x * _vertices[0].z;
734 pv.y = rpos.y + si.y * _vertices[0].x + sj.y * _vertices[0].y + sk.y * _vertices[0].z;
735 pv.z = rpos.z + si.z * _vertices[0].x + sj.z * _vertices[0].y + sk.z * _vertices[0].z; // _vertices[0] position rel to opv
736 rv.x = dot_product(ri, pv);
737 rv.y = dot_product(rj, pv);
738 rv.z = dot_product(rk, pv); // _vertices[0] position rel to opv in ijk
739 bounding_box_reset_to_vector(&result, rv);
740 }
741
743 for (i = 1; i < vertexCount; i++)
744 {
745 pv.x = rpos.x + si.x * _vertices[i].x + sj.x * _vertices[i].y + sk.x * _vertices[i].z;
746 pv.y = rpos.y + si.y * _vertices[i].x + sj.y * _vertices[i].y + sk.y * _vertices[i].z;
747 pv.z = rpos.z + si.z * _vertices[i].x + sj.z * _vertices[i].y + sk.z * _vertices[i].z;
748 rv.x = dot_product(ri, pv);
749 rv.y = dot_product(rj, pv);
750 rv.z = dot_product(rk, pv);
751 bounding_box_add_vector(&result, rv);
752 }
753
754 return result;
755}
756
757
758- (BoundingBox)findSubentityBoundingBoxWithPosition:(Vector)position rotMatrix:(OOMatrix)rotMatrix
759{
760 // HACK! Should work out what the various bounding box things do and make it neat and consistent.
761 // FIXME: this is a bottleneck.
762// Try to fix bottleneck by caching for common case where subentity
763// pos+rot is constant from frame to frame. - CIM
764
765 if (vector_equal(position,_lastPosition) && OOMatrixEqual(rotMatrix,_lastRotMatrix))
766 {
767 return _lastBoundingBox;
768 }
769
770 BoundingBox result;
771 Vector v;
772
773 v = vector_add(position, OOVectorMultiplyMatrix(_vertices[0], rotMatrix));
774 bounding_box_reset_to_vector(&result,v);
775
777 for (i = 1; i < vertexCount; i++)
778 {
779 v = vector_add(position, OOVectorMultiplyMatrix(_vertices[i], rotMatrix));
780 bounding_box_add_vector(&result,v);
781 }
782
783 _lastBoundingBox = result;
784 _lastPosition = position;
785 _lastRotMatrix = rotMatrix;
786
787 return result;
788}
789
790
791- (OOMesh *)meshRescaledBy:(GLfloat)scaleFactor
792{
793 OOMesh *result = [self mutableCopy];
794 [result rescaleByFactor:scaleFactor];
795 return [result autorelease];
796}
797
798
799- (void)setBindingTarget:(id<OOWeakReferenceSupport>)target
800{
801 unsigned i;
802
803 for (i = 0; i != kOOMeshMaxMaterials; ++i)
804 {
805 [materials[i] setBindingTarget:target];
806 }
807}
808
809
810#ifndef NDEBUG
811- (void)dumpSelfState
812{
813 [super dumpSelfState];
814
815 if (baseFile != nil) OOLog(@"dumpState.mesh", @"Model file: %@", baseFile);
816 OOLog(@"dumpState.mesh", @"Vertex count: %u, face count: %u", vertexCount, faceCount);
817 OOLog(@"dumpState.mesh", @"Normals: %@", NormalModeDescription(_normalMode));
818}
819#endif
820
821
822#ifndef NDEBUG
823- (NSSet *) allTextures
824{
825 NSMutableSet *result = [NSMutableSet set];
827 for (i = 0; i != materialCount; i++)
828 {
829 [result unionSet:[materials[i] allTextures]];
830 }
831
832 return result;
833}
834
835
836- (size_t) totalSize
837{
838 size_t result = [super totalSize];
839 if (_vertices != NULL) result += sizeof *_vertices * vertexCount;
840 if (_normals != NULL) result += sizeof *_normals * vertexCount;
841 if (_tangents != NULL) result += sizeof *_tangents * vertexCount;
842 if (_faces != NULL) result += sizeof *_faces * faceCount;
843
844 result += _displayLists.count * (sizeof (GLint) + sizeof (GLfloat) + sizeof (Vector) * 3);
845
847 for (i = 0; i != materialCount; i++)
848 {
849 result += [materials[i] oo_objectSize];
850 }
851
852 result += [octree totalSize];
853 return result;
854}
855#endif
856
857
858/* This method exists purely to suppress Clang static analyzer warnings that
859 these ivars are unused (but may be used by categories, which they are).
860 FIXME: there must be a feature macro we can use to avoid actually building
861 this into the app, but I can't find it in docs.
862*/
863- (BOOL) suppressClangStuff
864{
865 return _normals && _tangents && _faces && boundingBox.min.x;
866}
867
868@end
869
870
871@implementation OOMesh (Private)
872
873- (id)initWithName:(NSString *)name
874 cacheKey:(NSString *)cacheKey
875materialDictionary:(NSDictionary *)materialDict
876 shadersDictionary:(NSDictionary *)shadersDict
877 smooth:(BOOL)smooth
878 shaderMacros:(NSDictionary *)macros
879shaderBindingTarget:(id<OOWeakReferenceSupport>)target
880 scaleFactor:(float)scale
881 cacheWriteable:(BOOL)cacheWriteable
882{
884
885 self = [super init];
886 if (self == nil) return nil;
887
888 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
889 _normalMode = smooth ? kNormalModeSmooth : kNormalModePerFace;
890 _cacheWriteable = cacheWriteable;
891
892#if OOMESH_PROFILE
893 _stopwatch = [[OOProfilingStopwatch alloc] init];
894#endif
895
896 if ([self loadData:name scaleFactor:scale])
897 {
898 [self calculateBoundingVolumes];
899 PROFILE(@"finished calculateBoundingVolumes (again\?\?)");
900
901 baseFile = [name copy];
902 baseFileOctreeCacheRef = [[NSString stringWithFormat:@"%@-%.3f", baseFile, scale] copy];
903
904 /* New in r3033: save the material-defining parameters here so we
905 can rebind the materials at any time.
906 -- Ahruman 2010-02-17
907 */
908 _materialDict = [materialDict copy];
909 _shadersDict = [shadersDict copy];
910 _cacheKey = [cacheKey copy];
911 _shaderMacros = [macros copy];
912 _shaderBindingTarget = [target weakRetain];
913
914 [self rebindMaterials];
915 PROFILE(@"finished material setup");
916
918 }
919 else
920 {
921 [self release];
922 self = nil;
923 }
924#if OOMESH_PROFILE
925 DESTROY(_stopwatch);
926#endif
927#if OO_MULTITEXTURE
928 if (EXPECT(self != nil))
929 {
930 _textureUnitCount = NSNotFound;
931 }
932#endif
933
934 [pool release];
935 return self;
936
938}
939
940
941- (id)mutableCopyWithZone:(NSZone *)zone
942{
943 OOMesh *result = nil;
945
946 result = (OOMesh *)NSCopyObject(self, 0, zone);
947
948 if (result != nil)
949 {
950 [result->baseFile retain];
951 [result->baseFileOctreeCacheRef retain];
952 [result->octree retain];
953 [result->_retainedObjects retain];
954 [result->_materialDict retain];
955 [result->_shadersDict retain];
956 [result->_cacheKey retain];
957 [result->_shaderMacros retain];
958 [result->_shaderBindingTarget retain];
959
960 for (i = 0; i != kOOMeshMaxMaterials; ++i)
961 {
962 [result->materialKeys[i] retain];
963 [result->materials[i] retain];
964 }
965
966 // Reset unsharable GL state
967 result->listsReady = NO;
968
970 }
971
972 return result;
973}
974
975
976- (void) deleteDisplayLists
977{
978 if (listsReady)
979 {
981
982 OOGL(glDeleteLists(displayList0, materialCount));
983 listsReady = NO;
984 }
985}
986
987
988- (void) resetGraphicsState
989{
990 [self deleteDisplayLists];
991 [self rebindMaterials];
992 _textureUnitCount = NSNotFound;
993}
994
995
996- (NSDictionary *)modelData
997{
999
1000 NSNumber *vertCnt = nil,
1001 *faceCnt = nil;
1002 NSData *vertData = nil,
1003 *normData = nil,
1004 *tanData = nil,
1005 *faceData = nil;
1006 NSArray *mtlKeys = nil;
1007 NSNumber *normMode = nil;
1008
1009 BOOL includeNormals = IsPerVertexNormalMode(_normalMode);
1010
1011 // Prepare cache data elements.
1012 vertCnt = [NSNumber numberWithUnsignedInt:vertexCount];
1013 faceCnt = [NSNumber numberWithUnsignedInt:faceCount];
1014
1015 vertData = [_retainedObjects objectForKey:@"vertices"];
1016 faceData = [_retainedObjects objectForKey:@"faces"];
1017 if (includeNormals)
1018 {
1019 normData = [_retainedObjects objectForKey:@"normals"];
1020 tanData = [_retainedObjects objectForKey:@"tangents"];
1021 }
1022
1023 if (materialCount != 0)
1024 {
1025 mtlKeys = [NSArray arrayWithObjects:materialKeys count:materialCount];
1026 }
1027 else
1028 {
1029 mtlKeys = [NSArray array];
1030 }
1031 normMode = [NSNumber numberWithUnsignedChar:_normalMode];
1032
1033 // Ensure we have all the required data elements.
1034 if (vertCnt == nil ||
1035 faceCnt == nil ||
1036 vertData == nil ||
1037 faceData == nil ||
1038 mtlKeys == nil ||
1039 normMode == nil)
1040 {
1041 return nil;
1042 }
1043
1044 if (includeNormals)
1045 {
1046 if (normData == nil || tanData == nil) return nil;
1047 }
1048
1049 // All OK; stick 'em in a dictionary.
1050 return [NSDictionary dictionaryWithObjectsAndKeys:
1051 vertCnt, @"vertex count",
1052 vertData, @"vertex data",
1053 faceCnt, @"face count",
1054 faceData, @"face data",
1055 mtlKeys, @"material keys",
1056 normMode, @"normal mode",
1057 /* NOTE: order matters. Since normData and tanData
1058 are last, if they're nil the dictionary will be
1059 built without them, which is desired behaviour.
1060 */
1061 normData, @"normal data",
1062 tanData, @"tangent data",
1063 nil];
1064
1066}
1067
1068
1069- (BOOL)setModelFromModelData:(NSDictionary *)dict name:(NSString *)fileName
1070{
1072
1073 NSData *vertData = nil,
1074 *normData = nil,
1075 *tanData = nil,
1076 *faceData = nil;
1077 NSArray *mtlKeys = nil;
1078 NSString *key = nil;
1079 unsigned i;
1080
1081 if (dict == nil || ![dict isKindOfClass:[NSDictionary class]]) return NO;
1082
1083 vertexCount = [dict oo_unsignedIntForKey:@"vertex count"];
1084 faceCount = [dict oo_unsignedIntForKey:@"face count"];
1085
1086 if (vertexCount == 0 || faceCount == 0) return NO;
1087
1088 // Read data elements from dictionary.
1089 vertData = [dict oo_dataForKey:@"vertex data"];
1090 faceData = [dict oo_dataForKey:@"face data"];
1091
1092 mtlKeys = [dict oo_arrayForKey:@"material keys"];
1093 _normalMode = [dict oo_unsignedCharForKey:@"normal mode"];
1094 BOOL includeNormals = IsPerVertexNormalMode(_normalMode);
1095
1096 // Ensure we have all the required data elements.
1097 if (vertData == nil ||
1098 faceData == nil ||
1099 mtlKeys == nil)
1100 {
1101 OOLog(@"mesh.load.error.badCacheData", @"Ignoring bad cache data for mesh \"%@\".", fileName);
1102 return NO;
1103 }
1104
1105 if (includeNormals)
1106 {
1107 normData = [dict oo_dataForKey:@"normal data"];
1108 tanData = [dict oo_dataForKey:@"tangent data"];
1109 if (normData == nil || tanData == nil)
1110 {
1111 OOLog(@"mesh.load.error.badCacheData", @"Ignoring bad normal/tangent cache data for mesh \"%@\".", fileName);
1112 return NO;
1113 }
1114 }
1115
1116 // Ensure data objects are of correct size.
1117 if ([vertData length] != sizeof *_vertices * vertexCount) return NO;
1118 if ([faceData length] != sizeof *_faces * faceCount) return NO;
1119 if (includeNormals)
1120 {
1121 if ([normData length] != sizeof *_normals * vertexCount) return NO;
1122 if ([tanData length] != sizeof *_tangents * vertexCount) return NO;
1123 }
1124
1125 // Retain data.
1126 _vertices = (Vector *)[vertData bytes];
1127 [self setRetainedObject:vertData forKey:@"vertices"];
1128 _faces = (OOMeshFace *)[faceData bytes];
1129 [self setRetainedObject:faceData forKey:@"faces"];
1130 if (includeNormals)
1131 {
1132 _normals = (Vector *)[normData bytes];
1133 [self setRetainedObject:normData forKey:@"normals"];
1134 _tangents = (Vector *)[tanData bytes];
1135 [self setRetainedObject:tanData forKey:@"tangents"];
1136 }
1137 else
1138 {
1139 _normals = NULL;
1140 _tangents = NULL;
1141 }
1142
1143 // Copy material keys.
1144 materialCount = [mtlKeys count];
1145 for (i = 0; i != materialCount; ++i)
1146 {
1147 key = [mtlKeys oo_stringAtIndex:i];
1148 if (key != nil) materialKeys[i] = [key copy];
1149 else
1150 {
1151 OOLog(@"mesh.load.error.badCacheData", @"Ignoring bad cache data for mesh \"%@\".", fileName);
1152 return NO;
1153 }
1154 }
1155
1156 return YES;
1157
1159}
1160
1161
1162- (BOOL)loadData:(NSString *)filename scaleFactor:(float)scale
1163{
1165
1166 NSScanner *scanner;
1167 NSDictionary *cacheData = nil;
1168 BOOL failFlag = NO;
1169 NSString *failString = @"***** ";
1170 unsigned i, j;
1171 NSMutableDictionary *texFileName2Idx = nil;
1172 NSString *cacheKey = nil;
1173 BOOL using_preloaded = NO;
1174
1175 cacheKey = [NSString stringWithFormat:@"%@:%u:%.3f", filename, _normalMode, scale];
1176 cacheData = [OOCacheManager meshDataForName:cacheKey];
1177 if (cacheData != nil)
1178 {
1179 if ([self setModelFromModelData:cacheData name:filename])
1180 {
1181 using_preloaded = YES;
1182 PROFILE(@"loaded from cache");
1183 OOLog(@"mesh.load.cached", @"Retrieved mesh \"%@\" from cache.", filename);
1184 }
1185 }
1186
1187 if (!using_preloaded)
1188 {
1189 OOLog(@"mesh.load.uncached", @"Mesh \"%@\" is not in cache, loading.", cacheKey);
1190
1191 NSCharacterSet *whitespaceCharSet = [NSCharacterSet whitespaceCharacterSet];
1192 NSCharacterSet *whitespaceAndNewlineCharSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
1193#if OOLITE_MAC_OS_X
1194 NSCharacterSet *newlineCharSet = [NSCharacterSet newlineCharacterSet];
1195#else
1196 static NSCharacterSet *newlineCharSet = nil;
1197 if (newlineCharSet == nil)
1198 {
1199 NSMutableCharacterSet *temp = [[whitespaceAndNewlineCharSet mutableCopy] autorelease];
1200 [temp formIntersectionWithCharacterSet:[whitespaceCharSet invertedSet]];
1201 newlineCharSet = [temp copy];
1202 }
1203#endif
1204
1205 texFileName2Idx = [NSMutableDictionary dictionary];
1206
1207 {
1208 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1209 NSString *data = [ResourceManager stringFromFilesNamed:filename inFolder:@"Models" cache:NO];
1210 if (data == nil)
1211 {
1212 // Model not found
1213 OOLog(kOOLogMeshDataNotFound, @"***** ERROR: could not find %@", filename);
1214 OOStandardsError(@"Model file not found");
1215 return NO;
1216 }
1217
1218 // strip out comments and commas between values
1219 NSMutableArray *lines = [NSMutableArray arrayWithArray:[data componentsSeparatedByString:@"\n"]];
1220 for (i = 0; i < [lines count]; i++)
1221 {
1222 NSString *line = [lines objectAtIndex:i];
1223 NSArray *parts;
1224 //
1225 // comments
1226 //
1227 parts = [line componentsSeparatedByString:@"#"];
1228 line = [parts objectAtIndex:0];
1229 parts = [line componentsSeparatedByString:@"//"];
1230 line = [parts objectAtIndex:0];
1231 //
1232 // commas
1233 //
1234 line = [[line componentsSeparatedByString:@","] componentsJoinedByString:@" "];
1235 //
1236 [lines replaceObjectAtIndex:i withObject:line];
1237 }
1238
1239 data = [lines componentsJoinedByString:@"\n"];
1240 scanner = [NSScanner scannerWithString:data];
1241
1242 [scanner retain];
1243 [pool release];
1244 [scanner autorelease];
1245 }
1246
1247 PROFILE(@"finished preprocessing");
1248
1249 // get number of vertices
1250 //
1251 [scanner setScanLocation:0]; //reset
1252 if ([scanner scanString:@"NVERTS" intoString:NULL])
1253 {
1254 int n_v;
1255 if ([scanner scanInt:&n_v])
1256 vertexCount = n_v;
1257 else
1258 {
1259 failFlag = YES;
1260 failString = [NSString stringWithFormat:@"%@Failed to read value of NVERTS\n",failString];
1261 }
1262 }
1263 else
1264 {
1265 failFlag = YES;
1266 failString = [NSString stringWithFormat:@"%@Failed to read NVERTS\n",failString];
1267 }
1268
1269 if (![self allocateVertexBuffersWithCount:vertexCount])
1270 {
1271 OOLog(kOOLogAllocationFailure, @"***** ERROR: failed to allocate memory for model %@ (%u vertices).", filename, vertexCount);
1272 return NO;
1273 }
1274
1275 // get number of faces
1276 if ([scanner scanString:@"NFACES" intoString:NULL])
1277 {
1278 int n_f;
1279 if ([scanner scanInt:&n_f])
1280 {
1281 faceCount = n_f;
1282 }
1283 else
1284 {
1285 failFlag = YES;
1286 failString = [NSString stringWithFormat:@"%@Failed to read value of NFACES\n",failString];
1287 }
1288 }
1289 else
1290 {
1291 failFlag = YES;
1292 failString = [NSString stringWithFormat:@"%@Failed to read NFACES\n",failString];
1293 }
1294
1295 // Allocate face->vertex table.
1296 size_t faceRefSize = sizeof (VertexFaceRef) * vertexCount;
1297 VertexFaceRef *faceRefs = calloc(1, faceRefSize);
1298 if (faceRefs != NULL)
1299 {
1300 // use an NSData to effectively autorelease it.
1301 NSData *faceRefHolder = [NSData dataWithBytesNoCopy:faceRefs length:faceRefSize freeWhenDone:YES];
1302 if (faceRefHolder == nil)
1303 {
1304 free(faceRefs);
1305 faceRefs = NULL;
1306 }
1307 }
1308
1309 if (faceRefs == NULL || ![self allocateFaceBuffersWithCount:faceCount])
1310 {
1311 OOLog(kOOLogAllocationFailure, @"***** ERROR: failed to allocate memory for model %@ (%u vertices, %u faces).", filename, vertexCount, faceCount);
1312 return NO;
1313 }
1314
1315 // get vertex data
1316 if ([scanner scanString:@"VERTEX" intoString:NULL])
1317 {
1318 for (j = 0; j < vertexCount; j++)
1319 {
1320 float x, y, z;
1321 if (!failFlag)
1322 {
1323 if (![scanner scanFloat:&x]) failFlag = YES;
1324 if (![scanner scanFloat:&y]) failFlag = YES;
1325 if (![scanner scanFloat:&z]) failFlag = YES;
1326 if (!failFlag)
1327 {
1328 _vertices[j] = make_vector(x*scale, y*scale, z*scale);
1329 }
1330 else
1331 {
1332 failString = [NSString stringWithFormat:@"%@Failed to read a value for vertex[%d] in %@\n", failString, j, @"VERTEX"];
1333 }
1334 }
1335 }
1336 }
1337 else
1338 {
1339 failFlag = YES;
1340 failString = [NSString stringWithFormat:@"%@Failed to find VERTEX data\n",failString];
1341 }
1342
1343 // get face data
1344 if ([scanner scanString:@"FACES" intoString:NULL])
1345 {
1346 for (j = 0; j < faceCount; j++)
1347 {
1348 int r, g, b;
1349 float nx, ny, nz;
1350 int n_v;
1351 if (!failFlag)
1352 {
1353 // colors
1354 if (![scanner scanInt:&r]) failFlag = YES;
1355 if (![scanner scanInt:&g]) failFlag = YES;
1356 if (![scanner scanInt:&b]) failFlag = YES;
1357 if (!failFlag)
1358 {
1359 _faces[j].smoothGroup = r;
1360 }
1361 else
1362 {
1363 failString = [NSString stringWithFormat:@"%@Failed to read a color for face[%d] in FACES\n", failString, j];
1364 }
1365
1366 // normal
1367 if (![scanner scanFloat:&nx]) failFlag = YES;
1368 if (![scanner scanFloat:&ny]) failFlag = YES;
1369 if (![scanner scanFloat:&nz]) failFlag = YES;
1370 if (!failFlag)
1371 {
1372 _faces[j].normal = vector_normal(make_vector(nx, ny, nz));
1373 }
1374 else
1375 {
1376 failString = [NSString stringWithFormat:@"%@Failed to read a normal for face[%d] in FACES\n", failString, j];
1377 }
1378
1379 // vertices
1380 if ([scanner scanInt:&n_v])
1381 {
1382 if (n_v < 3)
1383 {
1384 failFlag = YES;
1385 failString = [NSString stringWithFormat:@"%@Face[%u] has fewer than three vertices.\n", failString, j];
1386 }
1387 else if (n_v > 3)
1388 {
1389 OOLogWARN(@"mesh.load.warning.nonTriangular", @"Face[%u] of %@ has %u vertices specified. Only the first three will be used.", j, baseFile, n_v);
1390 n_v = 3;
1391 }
1392 }
1393 else
1394 {
1395 failFlag = YES;
1396 failString = [NSString stringWithFormat:@"%@Failed to read number of vertices for face[%d] in FACES\n", failString, j];
1397 }
1398
1399 if (!failFlag)
1400 {
1401 int vi;
1402 for (i = 0; (int)i < n_v; i++)
1403 {
1404 if ([scanner scanInt:&vi])
1405 {
1406 _faces[j].vertex[i] = vi;
1407 if (faceRefs != NULL) VFRAddFace(&faceRefs[vi], j);
1408 }
1409 else
1410 {
1411 failFlag = YES;
1412 failString = [NSString stringWithFormat:@"%@Failed to read vertex[%d] for face[%d] in FACES\n", failString, i, j];
1413 }
1414 }
1415 }
1416 }
1417 }
1418 }
1419 else
1420 {
1421 failFlag = YES;
1422 failString = [NSString stringWithFormat:@"%@Failed to find FACES data\n",failString];
1423 }
1424
1425 // Get textures data.
1426 if ([scanner scanString:@"TEXTURES" intoString:NULL])
1427 {
1428 for (j = 0; j < faceCount; j++)
1429 {
1430 NSString *materialKey;
1431 float max_x, max_y;
1432 float s, t;
1433 if (!failFlag)
1434 {
1435 // materialKey
1436 //
1437 [scanner scanCharactersFromSet:whitespaceAndNewlineCharSet intoString:NULL];
1438 if (![scanner scanUpToCharactersFromSet:whitespaceCharSet intoString:&materialKey])
1439 {
1440 failFlag = YES;
1441 failString = [NSString stringWithFormat:@"%@Failed to read texture filename for face[%d] in TEXTURES\n", failString, j];
1442 }
1443 else
1444 {
1445 NSNumber *index = [texFileName2Idx objectForKey:materialKey];
1446 if (index != nil)
1447 {
1448 _faces[j].materialIndex = [index unsignedIntValue];
1449 }
1450 else
1451 {
1452 if (materialCount == kOOMeshMaxMaterials)
1453 {
1454 OOLog(kOOLogMeshTooManyMaterials, @"***** ERROR: model %@ has too many materials (maximum is %d)", filename, kOOMeshMaxMaterials);
1455 return NO;
1456 }
1457 _faces[j].materialIndex = materialCount;
1458 materialKeys[materialCount] = [materialKey retain];
1459 index = [NSNumber numberWithUnsignedInt:materialCount];
1460 [texFileName2Idx setObject:index forKey:materialKey];
1461 ++materialCount;
1462 }
1463 }
1464
1465 // texture size
1466 //
1467 if (!failFlag)
1468 {
1469 if (![scanner scanFloat:&max_x]) failFlag = YES;
1470 if (![scanner scanFloat:&max_y]) failFlag = YES;
1471 if (failFlag)
1472 failString = [NSString stringWithFormat:@"%@Failed to read texture size for max_x and max_y in face[%d] in TEXTURES\n", failString, j];
1473 }
1474
1475 // vertices
1476 //
1477 if (!failFlag)
1478 {
1479 for (i = 0; i < 3; i++)
1480 {
1481 if (![scanner scanFloat:&s]) failFlag = YES;
1482 if (![scanner scanFloat:&t]) failFlag = YES;
1483 if (!failFlag)
1484 {
1485 _faces[j].s[i] = s / max_x;
1486 _faces[j].t[i] = t / max_y;
1487 }
1488 else
1489 failString = [NSString stringWithFormat:@"%@Failed to read s t coordinates for vertex[%d] in face[%d] in TEXTURES\n", failString, i, j];
1490 }
1491 }
1492 }
1493 }
1494 }
1495 else
1496 {
1497 failFlag = YES;
1498 failString = [failString stringByAppendingString:@"Failed to find TEXTURES data (will use placeholder material)\n"];
1499 materialKeys[0] = @"_oo_placeholder_material";
1500 materialCount = 1;
1501
1502 for (j = 0; j < faceCount; j++)
1503 {
1504 _faces[j].materialIndex = 0;
1505 }
1506 }
1507
1508 if ([scanner scanString:@"NAMES" intoString:NULL])
1509 {
1510 unsigned int count;
1511 if (![scanner scanInt:(int *)&count])
1512 {
1513 failFlag = YES;
1514 failString = [failString stringByAppendingString:@"Expected count after NAMES\n"];
1515 }
1516 else
1517 {
1518 for (j = 0; j < count; j++)
1519 {
1520 NSString *name = nil;
1521 [scanner scanCharactersFromSet:whitespaceAndNewlineCharSet intoString:NULL];
1522 if (![scanner scanUpToCharactersFromSet:newlineCharSet intoString:&name])
1523 {
1524 failFlag = YES;
1525 failString = [failString stringByAppendingString:@"Expected file name\n"];
1526 }
1527 else
1528 {
1529 [self renameTexturesFrom:[NSString stringWithFormat:@"%u", j] to:name];
1530 }
1531 }
1532 }
1533 }
1534
1535 BOOL explicitTangents = NO;
1536
1537 // Get explicit normals.
1538 if ([scanner scanString:@"NORMALS" intoString:NULL])
1539 {
1540 _normalMode = kNormalModeExplicit;
1541 if (![self allocateNormalBuffersWithCount:vertexCount])
1542 {
1543 OOLog(kOOLogAllocationFailure, @"***** ERROR: failed to allocate memory for model %@ (%u vertices).", filename, vertexCount);
1544 return NO;
1545 }
1546
1547 for (j = 0; j < vertexCount; j++)
1548 {
1549 float x, y, z;
1550 if (!failFlag)
1551 {
1552 if (![scanner scanFloat:&x]) failFlag = YES;
1553 if (![scanner scanFloat:&y]) failFlag = YES;
1554 if (![scanner scanFloat:&z]) failFlag = YES;
1555 if (!failFlag)
1556 {
1557 _normals[j] = vector_normal(make_vector(x, y, z));
1558 }
1559 else
1560 {
1561 failString = [NSString stringWithFormat:@"%@Failed to read a value for vertex[%d] in %@\n", failString, j, @"NORMALS"];
1562 }
1563 }
1564 }
1565
1566 // Get explicit tangents (only together with vertices).
1567 if ([scanner scanString:@"TANGENTS" intoString:NULL])
1568 {
1569 for (j = 0; j < vertexCount; j++)
1570 {
1571 float x, y, z;
1572 if (!failFlag)
1573 {
1574 if (![scanner scanFloat:&x]) failFlag = YES;
1575 if (![scanner scanFloat:&y]) failFlag = YES;
1576 if (![scanner scanFloat:&z]) failFlag = YES;
1577 if (!failFlag)
1578 {
1579 _tangents[j] = vector_normal(make_vector(x, y, z));
1580 }
1581 else
1582 {
1583 failString = [NSString stringWithFormat:@"%@Failed to read a value for vertex[%d] in %@\n", failString, j, @"TANGENTS"];
1584 }
1585 }
1586 }
1587 }
1588 }
1589
1590 PROFILE(@"finished parsing");
1591
1592 if (IsLegacyNormalMode(_normalMode))
1593 {
1594 [self checkNormalsAndAdjustWinding];
1595 PROFILE(@"finished checkNormalsAndAdjustWinding");
1596 }
1597 if (!explicitTangents)
1598 {
1599 [self generateFaceTangents];
1600 PROFILE(@"finished generateFaceTangents");
1601 }
1602
1603 // check for smooth shading and recalculate normals
1604 if (_normalMode == kNormalModeSmooth)
1605 {
1606 if (![self allocateNormalBuffersWithCount:vertexCount])
1607 {
1608 OOLog(kOOLogAllocationFailure, @"***** ERROR: failed to allocate memory for model %@ (%u vertices).", filename, vertexCount);
1609 return NO;
1610 }
1611 [self calculateVertexNormalsAndTangentsWithFaceRefs:faceRefs];
1612 PROFILE(@"finished calculateVertexNormalsAndTangents");
1613
1614 }
1615 else if (IsPerVertexNormalMode(_normalMode) && !explicitTangents)
1616 {
1617 [self calculateVertexTangentsWithFaceRefs:faceRefs];
1618 PROFILE(@"finished calculateVertexTangents");
1619 }
1620
1621 // save the resulting data for possible reuse
1622 if (EXPECT(_cacheWriteable))
1623 {
1624 [OOCacheManager setMeshData:[self modelData] forName:cacheKey];
1625 PROFILE(@"saved to cache");
1626 }
1627
1628 if (failFlag)
1629 {
1630 OOLog(@"mesh.error", @"%@ ..... from %@ %@", failString, filename, (using_preloaded)? @"(from preloaded data)" :@"(from file)");
1631 }
1632 }
1633
1634 [self calculateBoundingVolumes];
1635 PROFILE(@"finished calculateBoundingVolumes");
1636
1637 // set up vertex arrays for drawing
1638 if (![self setUpVertexArrays]) return NO;
1639 PROFILE(@"finished setUpVertexArrays");
1640
1641 return YES;
1642
1644}
1645
1646
1647- (void) checkNormalsAndAdjustWinding
1648{
1650
1651 Vector calculatedNormal;
1653
1654 NSParameterAssert(_normalMode != kNormalModeExplicit);
1655
1656 for (i = 0; i < faceCount; i++)
1657 {
1658 Vector v0, v1, v2, norm;
1659 v0 = _vertices[_faces[i].vertex[0]];
1660 v1 = _vertices[_faces[i].vertex[1]];
1661 v2 = _vertices[_faces[i].vertex[2]];
1662 norm = _faces[i].normal;
1663
1664 calculatedNormal = normal_to_surface(v2, v1, v0);
1665 if (vector_equal(norm, kZeroVector))
1666 {
1667 norm = vector_flip(calculatedNormal);
1668 _faces[i].normal = norm;
1669 }
1670
1671 /* FIXME: for 2.0, either require explicit normals for every model
1672 or change to: if (dot_product(norm, calculatedNormal) < 0.0f)
1673 -- Ahruman 2010-01-23
1674 */
1675 if (norm.x * calculatedNormal.x < 0 || norm.y * calculatedNormal.y < 0 || norm.z * calculatedNormal.z < 0)
1676 {
1677 // normal lies in the WRONG direction!
1678 // reverse the winding
1679 int v0 = _faces[i].vertex[0];
1680 _faces[i].vertex[0] = _faces[i].vertex[2];
1681 _faces[i].vertex[2] = v0;
1682
1683 GLfloat f0 = _faces[i].s[0];
1684 _faces[i].s[0] = _faces[i].s[2];
1685 _faces[i].s[2] = f0;
1686
1687 f0 = _faces[i].t[0];
1688 _faces[i].t[0] = _faces[i].t[2];
1689 _faces[i].t[2] = f0;
1690 }
1691 }
1692
1694}
1695
1696
1697- (void) generateFaceTangents
1698{
1700
1702 for (i = 0; i < faceCount; i++)
1703 {
1704 OOMeshFace *face = _faces + i;
1705
1706 /* Generate tangents, i.e. vectors that run in the direction of the s
1707 texture coordinate. Based on code I found in a forum somewhere and
1708 then lost track of. Sorry to whomever I should be crediting.
1709 -- Ahruman 2008-11-23
1710 */
1711 Vector vAB = vector_subtract(_vertices[face->vertex[1]], _vertices[face->vertex[0]]);
1712 Vector vAC = vector_subtract(_vertices[face->vertex[2]], _vertices[face->vertex[0]]);
1713 Vector nA = face->normal;
1714
1715 // projAB = aB - (nA . vAB) * nA
1716 Vector vProjAB = vector_subtract(vAB, vector_multiply_scalar(nA, dot_product(nA, vAB)));
1717 Vector vProjAC = vector_subtract(vAC, vector_multiply_scalar(nA, dot_product(nA, vAC)));
1718
1719 // delta s/t
1720 GLfloat dsAB = face->s[1] - face->s[0];
1721 GLfloat dsAC = face->s[2] - face->s[0];
1722 GLfloat dtAB = face->t[1] - face->t[0];
1723 GLfloat dtAC = face->t[2] - face->t[0];
1724
1725 if (dsAC * dtAB > dsAB * dtAC)
1726 {
1727 face->tangent = vector_normal(vector_subtract(vector_multiply_scalar(vProjAC, dtAB), vector_multiply_scalar(vProjAB, dtAC)));
1728 }
1729 else
1730 {
1731 face->tangent = vector_normal(vector_subtract(vector_multiply_scalar(vProjAB, dtAC), vector_multiply_scalar(vProjAC, dtAB)));
1732 }
1733 }
1734
1736}
1737
1738
1739static float FaceArea(GLuint *vertIndices, Vector *vertices)
1740{
1741 /* Calculate areas using Heron's formula. */
1742 float a2 = distance2(vertices[vertIndices[0]], vertices[vertIndices[1]]);
1743 float b2 = distance2(vertices[vertIndices[1]], vertices[vertIndices[2]]);
1744 float c2 = distance2(vertices[vertIndices[2]], vertices[vertIndices[0]]);
1745 return sqrt((2.0f * (a2 * b2 + b2 * c2 + c2 * a2) - (a2 * a2 + b2 * b2 +c2 * c2)) * 0.0625f);
1746}
1747
1748
1749- (void) calculateVertexNormalsAndTangentsWithFaceRefs:(VertexFaceRef *)faceRefs
1750{
1752
1753 NSParameterAssert(faceRefs != NULL);
1754
1755 NSUInteger i,j;
1756 float triangle_area[faceCount];
1757
1758 NSAssert1(_normals != NULL && _tangents != NULL, @"Normal/tangent buffers not allocated in %s", __PRETTY_FUNCTION__);
1759
1760 for (i = 0 ; i < faceCount; i++)
1761 {
1762 triangle_area[i] = FaceArea(_faces[i].vertex, _vertices);
1763 }
1764 for (i = 0; i < vertexCount; i++)
1765 {
1766 Vector normal_sum = kZeroVector;
1767 Vector tangent_sum = kZeroVector;
1768
1769 VertexFaceRef *vfr = &faceRefs[i];
1770 NSUInteger fIter, fCount = VFRGetCount(vfr);
1771 for (fIter = 0; fIter < fCount; fIter++)
1772 {
1773 j = VFRGetFaceAtIndex(vfr, fIter);
1774
1775 float t = triangle_area[j]; // weight sum by area
1776 normal_sum = vector_add(normal_sum, vector_multiply_scalar(_faces[j].normal, t));
1777 tangent_sum = vector_add(tangent_sum, vector_multiply_scalar(_faces[j].tangent, t));
1778 }
1779
1780 normal_sum = vector_normal_or_fallback(normal_sum, kBasisZVector);
1781 tangent_sum = vector_subtract(tangent_sum, vector_multiply_scalar(normal_sum, dot_product(tangent_sum, normal_sum)));
1782 tangent_sum = vector_normal_or_fallback(tangent_sum, kBasisXVector);
1783
1784 _normals[i] = normal_sum;
1785 _tangents[i] = tangent_sum;
1786 }
1787
1789}
1790
1791
1792static float FaceAreaCorrect(GLuint *vertIndices, Vector *vertices)
1793{
1794 /* Calculate area of triangle.
1795 The magnitude of the cross product of two vectors is the area of
1796 the parallelogram they span. The area of a triangle is half the
1797 area of a parallelogram sharing two of its sides.
1798 Since we only use the area of the triangle as a weight factor,
1799 constant terms are irrelevant, so we don't bother halving the
1800 value.
1801 */
1802 Vector AB = vector_subtract(vertices[vertIndices[1]], vertices[vertIndices[0]]);
1803 Vector AC = vector_subtract(vertices[vertIndices[2]], vertices[vertIndices[0]]);
1804 return magnitude(true_cross_product(AB, AC));
1805}
1806
1807
1808- (void) calculateVertexTangentsWithFaceRefs:(VertexFaceRef *)faceRefs
1809{
1811
1812 NSParameterAssert(faceRefs != NULL);
1813
1814 /* This is conceptually broken.
1815 At the moment, it's calculating one tangent per "input" vertex. It should
1816 be calculating one tangent per "real" vertex, where a "real" vertex is
1817 defined as a combination of position, normal, material and texture
1818 coordinates.
1819 Currently, we don't have a format with unique "real" vertices.
1820 This basically means explicit-normal models without explicit tangents
1821 can't usefully be normal mapped.
1822 I don't intend to do anything about this pre-MSNR, although it might be
1823 possible to fix it by moving tangent generation to the same stage as
1824 smooth-grouped normal generation.
1825 -- Ahruman 2010-05-22
1826 */
1827 NSUInteger i,j;
1828 float triangle_area[faceCount];
1829 for (i = 0 ; i < faceCount; i++)
1830 {
1831 triangle_area[i] = FaceAreaCorrect(_faces[i].vertex, _vertices);
1832 }
1833 for (i = 0; i < vertexCount; i++)
1834 {
1835 Vector tangent_sum = kZeroVector;
1836
1837 VertexFaceRef *vfr = &faceRefs[i];
1838 NSUInteger fIter, fCount = VFRGetCount(vfr);
1839 for (fIter = 0; fIter < fCount; fIter++)
1840 {
1841 j = VFRGetFaceAtIndex(vfr, fIter);
1842
1843 float t = triangle_area[j]; // weight sum by area
1844 tangent_sum = vector_add(tangent_sum, vector_multiply_scalar(_faces[j].tangent, t));
1845 }
1846
1847 tangent_sum = vector_subtract(tangent_sum, vector_multiply_scalar(_normals[i], dot_product(_normals[i], tangent_sum)));
1848 tangent_sum = vector_normal_or_fallback(tangent_sum, kBasisXVector);
1849
1850 _tangents[i] = tangent_sum;
1851 }
1852
1854}
1855
1856/* profiling suggests this function takes a lot of time - almost all
1857 * the overhead of setting up a new ship is here. - CIM */
1858- (void) getNormal:(Vector *)outNormal andTangent:(Vector *)outTangent forVertex:(OOMeshVertexCount)v_index inSmoothGroup:(OOMeshSmoothGroup)smoothGroup
1859{
1861
1862 assert(outNormal != NULL && outTangent != NULL);
1863
1864 NSUInteger j;
1865 Vector normal_sum = kZeroVector;
1866 Vector tangent_sum = kZeroVector;
1867 for (j = 0; j < faceCount; j++)
1868 {
1869 if (_faces[j].smoothGroup == smoothGroup)
1870 {
1871 if ((_faces[j].vertex[0] == v_index)||(_faces[j].vertex[1] == v_index)||(_faces[j].vertex[2] == v_index))
1872 {
1873 float area = FaceArea(_faces[j].vertex, _vertices);
1874 normal_sum = vector_add(normal_sum, vector_multiply_scalar(_faces[j].normal, area));
1875 tangent_sum = vector_add(tangent_sum, vector_multiply_scalar(_faces[j].tangent, area));
1876 }
1877 }
1878 }
1879
1880 *outNormal = vector_normal_or_fallback(normal_sum, kBasisZVector);
1881 *outTangent = vector_normal_or_fallback(tangent_sum, kBasisXVector);
1882
1884}
1885
1886
1887- (BOOL) setUpVertexArrays
1888{
1890
1891 NSUInteger fi, vi, mi;
1892
1893 if (![self allocateVertexArrayBuffersWithCount:faceCount]) return NO;
1894
1895 // if smoothed, find any vertices that are between faces of different
1896 // smoothing groups and mark them as being on an edge and therefore NOT
1897 // smooth shaded
1898 BOOL is_edge_vertex[vertexCount];
1899 GLfloat smoothGroup[vertexCount];
1900 for (vi = 0; vi < vertexCount; vi++)
1901 {
1902 is_edge_vertex[vi] = NO;
1903 smoothGroup[vi] = -1;
1904 }
1905 if (_normalMode == kNormalModeSmooth)
1906 {
1907 for (fi = 0; fi < faceCount; fi++)
1908 {
1909 GLfloat rv = _faces[fi].smoothGroup;
1910 int i;
1911 for (i = 0; i < 3; i++)
1912 {
1913 vi = _faces[fi].vertex[i];
1914 if (smoothGroup[vi] < 0.0) // unassigned
1915 smoothGroup[vi] = rv;
1916 else if (smoothGroup[vi] != rv) // a different colour
1917 is_edge_vertex[vi] = YES;
1918 }
1919 }
1920 }
1921
1922
1923 // base model, flat or smooth shaded, all triangles
1924 int tri_index = 0;
1925 int uv_index = 0;
1926 int vertex_index = 0;
1927
1928 // Iterate over material names
1929 for (mi = 0; mi != materialCount; ++mi)
1930 {
1931 triangle_range[mi].location = tri_index;
1932
1933 for (fi = 0; fi < faceCount; fi++)
1934 {
1935 Vector normal, tangent;
1936
1937 if (_faces[fi].materialIndex == mi)
1938 {
1939 for (vi = 0; vi < 3; vi++)
1940 {
1941 int v = _faces[fi].vertex[vi];
1942 if (IsPerVertexNormalMode(_normalMode))
1943 {
1944 if (is_edge_vertex[v])
1945 {
1946 [self getNormal:&normal andTangent:&tangent forVertex:v inSmoothGroup:_faces[fi].smoothGroup];
1947 }
1948 else
1949 {
1950 NSAssert1(_normals != NULL && _tangents != NULL, @"Normal/tangent buffers not allocated in %s", __PRETTY_FUNCTION__);
1951
1952 normal = _normals[v];
1953 tangent = _tangents[v];
1954 }
1955 }
1956 else
1957 {
1958 normal = _faces[fi].normal;
1959 tangent = _faces[fi].tangent;
1960 }
1961
1962 // FIXME: avoid redundant vertices so index array is actually useful.
1963 _displayLists.indexArray[tri_index++] = vertex_index;
1964 _displayLists.normalArray[vertex_index] = normal;
1965 _displayLists.tangentArray[vertex_index] = tangent;
1966 _displayLists.vertexArray[vertex_index++] = _vertices[v];
1967 _displayLists.textureUVArray[uv_index++] = _faces[fi].s[vi];
1968 _displayLists.textureUVArray[uv_index++] = _faces[fi].t[vi];
1969 }
1970 }
1971 }
1972 triangle_range[mi].length = tri_index - triangle_range[mi].location;
1973 }
1974
1975 _displayLists.count = tri_index; // total number of triangle vertices
1976 return YES;
1977
1979}
1980
1981
1982- (void) calculateBoundingVolumes
1983{
1985
1987 float d_squared, length_longest_axis, length_shortest_axis;
1988 GLfloat result;
1989
1990 result = 0.0f;
1991 if (vertexCount) bounding_box_reset_to_vector(&boundingBox, _vertices[0]);
1992 else bounding_box_reset(&boundingBox);
1993
1994 for (i = 0; i < vertexCount; i++)
1995 {
1996 d_squared = magnitude2(_vertices[i]);
1997 if (d_squared > result) result = d_squared;
1998 bounding_box_add_vector(&boundingBox, _vertices[i]);
1999 }
2000
2001 length_longest_axis = boundingBox.max.x - boundingBox.min.x;
2002 if (boundingBox.max.y - boundingBox.min.y > length_longest_axis)
2003 length_longest_axis = boundingBox.max.y - boundingBox.min.y;
2004 if (boundingBox.max.z - boundingBox.min.z > length_longest_axis)
2005 length_longest_axis = boundingBox.max.z - boundingBox.min.z;
2006
2007 length_shortest_axis = boundingBox.max.x - boundingBox.min.x;
2008 if (boundingBox.max.y - boundingBox.min.y < length_shortest_axis)
2009 length_shortest_axis = boundingBox.max.y - boundingBox.min.y;
2010 if (boundingBox.max.z - boundingBox.min.z < length_shortest_axis)
2011 length_shortest_axis = boundingBox.max.z - boundingBox.min.z;
2012
2013 d_squared = (length_longest_axis + length_shortest_axis) * (length_longest_axis + length_shortest_axis) * 0.25; // square of average length
2014 maxDrawDistance = d_squared * NO_DRAW_DISTANCE_FACTOR * NO_DRAW_DISTANCE_FACTOR; // no longer based on the collision radius
2015
2016 collisionRadius = sqrtf(result);
2017
2019}
2020
2021
2022- (void) rescaleByFactor:(GLfloat)factor
2023{
2024 // Rescale base vertices used for geometry calculations.
2026 Vector *vertex = NULL;
2027
2028 for (i = 0; i < vertexCount; i++)
2029 {
2030 vertex = &_vertices[i];
2031 *vertex = vector_multiply_scalar(*vertex, factor);
2032 }
2033
2034 // Rescale actual display vertices.
2035 for (i = 0; i < _displayLists.count; i++)
2036 {
2037 vertex = &_displayLists.vertexArray[i];
2038 *vertex = vector_multiply_scalar(*vertex, factor);
2039 }
2040
2041 [self calculateBoundingVolumes];
2042 DESTROY(octree);
2043 DESTROY(baseFile); // Avoid octree cache.
2044 DESTROY(baseFileOctreeCacheRef);
2045}
2046
2047
2048- (BoundingBox)boundingBox
2049{
2050 return boundingBox;
2051}
2052
2053
2054#ifndef NDEBUG
2055- (void)debugDrawNormals
2056{
2057 GLuint i;
2058 Vector v, n, t, b;
2059 float length, blend;
2060 GLfloat color[3];
2061 OODebugWFState state;
2062
2064
2065 state = OODebugBeginWireframe(NO);
2066
2067 // Draw
2068 OOGLBEGIN(GL_LINES);
2069 for (i = 0; i < _displayLists.count; ++i)
2070 {
2071 v = _displayLists.vertexArray[i];
2072 n = _displayLists.normalArray[i];
2073 t = _displayLists.tangentArray[i];
2074 b = true_cross_product(n, t);
2075
2076 // Draw normal
2077 length = magnitude2(n);
2078 blend = fabs(length - 1) * 5.0;
2079 color[0] = MIN(blend, 1.0f);
2080 color[1] = 1.0f - color[0];
2081 color[2] = color[1];
2082 glColor3fv(color);
2083
2084 glVertex3f(v.x, v.y, v.z);
2085 scale_vector(&n, 5.0f);
2086 n = vector_add(n, v);
2087 glVertex3f(n.x, n.y, n.z);
2088
2089 // Draw tangent
2090 glColor3f(1.0f, 1.0f, 0.0f);
2091 t = vector_add(v, vector_multiply_scalar(t, 3.0f));
2092 glVertex3f(v.x, v.y, v.z);
2093 glVertex3f(t.x, t.y, t.z);
2094
2095 // Draw bitangent
2096 glColor3f(0.0f, 1.0f, 0.0f);
2097 b = vector_add(v, vector_multiply_scalar(b, 3.0f));
2098 glVertex3f(v.x, v.y, v.z);
2099 glVertex3f(b.x, b.y, b.z);
2100 }
2101 OOGLEND();
2102
2103 OODebugEndWireframe(state);
2104}
2105#endif
2106
2107
2108- (void) setRetainedObject:(id)object forKey:(NSString *)key
2109{
2110 assert(key != nil);
2111
2112 if (object != nil)
2113 {
2114 if (_retainedObjects == nil) _retainedObjects = [[NSMutableDictionary alloc] init];
2115 [_retainedObjects setObject:object forKey:key];
2116 }
2117}
2118
2119
2120#if SCRIBBLE
2121static void Scribble(void *bytes, size_t size)
2122{
2123 #if OOLITE_BIG_ENDIAN
2124 enum { kScribble = 0xFEEDFACE };
2125 #else
2126 enum { kScribble = 0xCEFAEDFE };
2127 #endif
2128
2129 size /= sizeof (uint32_t);
2130 uint32_t *mem = bytes;
2131 while (size--) *mem++ = kScribble;
2132}
2133#else
2134#define Scribble(bytes, size) do {} while (0)
2135#endif
2136
2137
2138/* valgrind complains that the memory allocated here isn't initialised
2139 at the time OOCacheManager::writeDict is pushing it to the
2140 cache. Not sure if that's a problem or not. - CIM */
2141- (void *) allocateBytesWithSize:(size_t)size count:(NSUInteger)count key:(NSString *)key
2142{
2143 if (count == 0) { count=1; }
2144 size *= count;
2145 void *bytes = malloc(size);
2146 if (bytes != NULL)
2147 {
2148 Scribble(bytes, size);
2149 NSData *holder = [NSData dataWithBytesNoCopy:bytes length:size freeWhenDone:YES];
2150 [self setRetainedObject:holder forKey:key];
2151 }
2152 return bytes;
2153}
2154
2155
2156- (BOOL) allocateVertexBuffersWithCount:(NSUInteger)count
2157{
2158 _vertices = [self allocateBytesWithSize:sizeof *_vertices count:vertexCount key:@"vertices"];
2159 return _vertices != NULL;
2160}
2161
2162
2163- (BOOL) allocateNormalBuffersWithCount:(NSUInteger)count
2164{
2165 _normals = [self allocateBytesWithSize:sizeof *_normals count:vertexCount key:@"normals"];
2166 _tangents = [self allocateBytesWithSize:sizeof *_tangents count:vertexCount key:@"tangents"];
2167 return _normals != NULL && _tangents != NULL;
2168}
2169
2170
2171- (BOOL) allocateFaceBuffersWithCount:(NSUInteger)count
2172{
2173 _faces = [self allocateBytesWithSize:sizeof *_faces count:faceCount key:@"faces"];
2174 return _faces != NULL;
2175}
2176
2177
2178- (BOOL) allocateVertexArrayBuffersWithCount:(NSUInteger)count
2179{
2180 _displayLists.indexArray = [self allocateBytesWithSize:sizeof *_displayLists.indexArray count:count * 3 key:@"indexArray"];
2181 _displayLists.textureUVArray = [self allocateBytesWithSize:sizeof *_displayLists.textureUVArray count:count * 6 key:@"textureUVArray"];
2182 _displayLists.vertexArray = [self allocateBytesWithSize:sizeof *_displayLists.vertexArray count:count * 3 key:@"vertexArray"];
2183 _displayLists.normalArray = [self allocateBytesWithSize:sizeof *_displayLists.normalArray count:count * 3 key:@"normalArray"];
2184 _displayLists.tangentArray = [self allocateBytesWithSize:sizeof *_displayLists.tangentArray count:count * 3 key:@"tangentArray"];
2185
2186 return _faces != NULL &&
2187 _displayLists.indexArray != NULL &&
2188 _displayLists.textureUVArray != NULL &&
2189 _displayLists.vertexArray != NULL &&
2190 _displayLists.normalArray != NULL &&
2191 _displayLists.tangentArray != NULL;
2192}
2193
2194
2195- (void) renameTexturesFrom:(NSString *)from to:(NSString *)to
2196{
2197 /* IMPORTANT: this has to be called before setUpMaterials..., so it can
2198 only be used during loading.
2199 */
2201 for (i = 0; i != materialCount; i++)
2202 {
2203 if ([materialKeys[i] isEqualToString:from])
2204 {
2205 [materialKeys[i] release];
2206 materialKeys[i] = [to copy];
2207 }
2208 }
2209}
2210
2211@end
2212
2213
2214static NSString * const kOOCacheMeshes = @"OOMesh";
2215
2216@implementation OOCacheManager (OOMesh)
2217
2218+ (NSDictionary *)meshDataForName:(NSString *)inShipName
2219{
2220 return [[self sharedCache] objectForKey:inShipName inCache:kOOCacheMeshes];
2221}
2222
2223
2224+ (void)setMeshData:(NSDictionary *)inData forName:(NSString *)inShipName
2225{
2226 if (inData != nil && inShipName != nil)
2227 {
2228 [[self sharedCache] setObject:inData forKey:inShipName inCache:kOOCacheMeshes];
2229 }
2230}
2231
2232@end
2233
2234
2235static NSString * const kOOCacheOctrees = @"octrees";
2236
2237@implementation OOCacheManager (Octree)
2238
2239+ (Octree *)octreeForModel:(NSString *)inKey
2240{
2241 NSDictionary *dict = nil;
2242 Octree *result = nil;
2243
2244 if (inKey == nil) return nil;
2245
2246 dict = [[self sharedCache] objectForKey:inKey inCache:kOOCacheOctrees];
2247 if (dict != nil)
2248 {
2249 result = [[Octree alloc] initWithDictionary:dict];
2250 [result autorelease];
2251 }
2252
2253 return result;
2254}
2255
2256
2257+ (void)setOctree:(Octree *)inOctree forModel:(NSString *)inKey
2258{
2259 if (inOctree != nil && inKey != nil)
2260 {
2261 [[self sharedCache] setObject:[inOctree dictionaryRepresentation] forKey:inKey inCache:kOOCacheOctrees];
2262 }
2263}
2264
2265
2266static void VFRAddFace(VertexFaceRef *vfr, NSUInteger index)
2267{
2268 NSCParameterAssert(vfr != NULL);
2269
2270 if (index < UINT16_MAX && vfr->internCount < kVertexFaceDefInternalCount)
2271 {
2272 vfr->internFaces[vfr->internCount++] = index;
2273 }
2274 else
2275 {
2276 if (vfr->extra == nil) vfr->extra = [NSMutableArray array];
2277 [vfr->extra addObject:[NSNumber numberWithInteger:index]];
2278 }
2279}
2280
2281
2282static NSUInteger VFRGetCount(VertexFaceRef *vfr)
2283{
2284 NSCParameterAssert(vfr != NULL);
2285
2286 return vfr->internCount + [vfr->extra count];
2287}
2288
2289
2290static NSUInteger VFRGetFaceAtIndex(VertexFaceRef *vfr, NSUInteger index)
2291{
2292 NSCParameterAssert(vfr != NULL && index < VFRGetCount(vfr));
2293
2294 if (index < vfr->internCount) return vfr->internFaces[index];
2295 else return [vfr->extra oo_unsignedIntegerAtIndex:index - vfr->internCount];
2296}
2297
2298@end
NSUInteger gDebugFlags
Definition main.m:7
#define NO_DRAW_DISTANCE_FACTOR
Definition Entity.h:46
#define DESTROY(x)
Definition OOCocoa.h:77
@ DEBUG_OCTREE_DRAW
@ DEBUG_DRAW_NORMALS
void OODebugEndWireframe(OODebugWFState state)
OODebugWFState OODebugBeginWireframe(BOOL ignoreZ)
void OOStandardsError(NSString *message)
#define EXPECT_NOT(x)
#define EXPECT(x)
#define OOJS_PROFILE_EXIT
#define OOJS_PROFILE_EXIT_VOID
#define OOJS_PROFILE_ENTER
#define OOLogWARN(class, format,...)
Definition OOLogging.h:113
NSString *const kOOLogException
Definition OOLogging.m:651
#define OOLog(class, format,...)
Definition OOLogging.h:88
NSString *const kOOLogAllocationFailure
Definition OOLogging.m:649
#define OO_ENTER_OPENGL()
#define MIN(A, B)
Definition OOMaths.h:111
bool OOMatrixEqual(OOMatrix a, OOMatrix b)
Definition OOMatrix.m:96
Vector OOVectorMultiplyMatrix(Vector v, OOMatrix m)
Definition OOMatrix.m:129
const OOMatrix kZeroMatrix
Definition OOMatrix.m:38
@ kOOMeshMaxMaterials
Definition OOMesh.h:53
uint16_t OOMeshSmoothGroup
Definition OOMesh.h:57
uint32_t OOMeshFaceCount
Definition OOMesh.h:60
uint32_t OOMeshVertexCount
Definition OOMesh.h:59
uint8_t OOMeshMaterialIndex
Definition OOMesh.h:58
uint8_t OOMeshMaterialCount
Definition OOMesh.h:58
static void VFRAddFace(VertexFaceRef *vfr, NSUInteger index)
static NSUInteger VFRGetCount(VertexFaceRef *vfr)
static BOOL IsLegacyNormalMode(OOMeshNormalMode mode)
Definition OOMesh.m:188
static NSString *const kOOLogMeshTooManyMaterials
Definition OOMesh.m:85
@ kVertexFaceDefInternalCount
Definition OOMesh.m:113
static NSString *const kOOCacheOctrees
Definition OOMesh.m:2235
static NSString *const kOOLogMeshDataNotFound
Definition OOMesh.m:84
#define PROFILE(tag)
Definition OOMesh.m:97
struct VertexFaceRef VertexFaceRef
static BOOL IsPerVertexNormalMode(OOMeshNormalMode mode)
Definition OOMesh.m:210
OOMeshNormalMode
Definition OOMesh.m:77
@ kNormalModeExplicit
Definition OOMesh.m:80
@ kNormalModeSmooth
Definition OOMesh.m:79
@ kNormalModePerFace
Definition OOMesh.m:78
#define Scribble(bytes, size)
Definition OOMesh.m:2134
static NSUInteger VFRGetFaceAtIndex(VertexFaceRef *vfr, NSUInteger index)
@ kBaseOctreeDepth
Definition OOMesh.m:68
@ kOctreeSmallSizeThreshold
Definition OOMesh.m:72
@ kSmallOctreeDepth
Definition OOMesh.m:70
@ kOctreeSizeThreshold
Definition OOMesh.m:71
static NSString *const kOOCacheMeshes
Definition OOMesh.m:2214
#define OOGLBEGIN
Definition OOOpenGL.h:253
@ OPENGL_STATE_OPAQUE
Definition OOOpenGL.h:123
#define OOVerifyOpenGLState()
Definition OOOpenGL.h:136
BOOL OOCheckOpenGLErrors(NSString *format,...)
Definition OOOpenGL.m:39
#define OOSetOpenGLState(STATE)
Definition OOOpenGL.h:135
#define OOGL(statement)
Definition OOOpenGL.h:251
#define OOGLEND
Definition OOOpenGL.h:254
unsigned count
return nil
float y
float x
double OOTimeDelta
Definition OOTypes.h:224
const BoundingBox kZeroBoundingBox
Definition OOVector.m:38
const Vector kZeroVector
Definition OOVector.m:28
const Vector kBasisZVector
Definition OOVector.m:31
const Vector kBasisXVector
Definition OOVector.m:29
@ kMaxOctreeDepth
Definition Octree.h:88
#define UNIVERSE
Definition Universe.h:833
static void VFRAddFace(VertexFaceRef *vfr, NSUInteger index)
Definition OOMesh.m:2266
static NSUInteger VFRGetFaceAtIndex(VertexFaceRef *vfr, NSUInteger index)
Definition OOMesh.m:2290
static NSUInteger VFRGetCount(VertexFaceRef *vfr)
Definition OOMesh.m:2282
BOOL setUpVertexArrays()
Definition OOMesh.m:1887
void deleteDisplayLists()
Definition OOMesh.m:976
void debugDrawNormals()
Definition OOMesh.m:2055
void generateFaceTangents()
Definition OOMesh.m:1697
void checkNormalsAndAdjustWinding()
Definition OOMesh.m:1647
NSDictionary * modelData()
Definition OOMesh.m:996
void calculateBoundingVolumes()
Definition OOMesh.m:1982
NSDictionary * meshDataForName:(NSString *inShipName)
Definition OOMesh.m:2218
void setOctree:forModel:(Octree *inOctree,[forModel] NSString *inKey)
Definition OOMesh.m:2257
Octree * octreeForModel:(NSString *inKey)
Definition OOMesh.m:2239
void setMeshData:forName:(NSDictionary *inData,[forName] NSString *inShipName)
Definition OOMesh.m:2224
void registerClient:(id< OOGraphicsResetClient > client)
void unregisterClient:(id< OOGraphicsResetClient > client)
OOGraphicsResetManager * sharedManager()
OOMaterial * materialWithName:cacheKey:materialDictionary:shadersDictionary:macros:bindingTarget:forSmoothedMesh:(NSString *name,[cacheKey] NSString *cacheKey,[materialDictionary] NSDictionary *materialDict,[shadersDictionary] NSDictionary *shadersDict,[macros] NSDictionary *macros,[bindingTarget] id< OOWeakReferenceSupport > object,[forSmoothedMesh] BOOL smooth)
void applyNone()
Definition OOMaterial.m:80
Octree * findOctreeToDepth:(NSUInteger depth)
instancetype converterWithCapacity:(NSUInteger capacity)
OOMaterial * placeholderMaterial()
Definition OOMesh.m:274
uint8_t listsReady
Definition OOMesh.h:95
void rescaleByFactor:(GLfloat factor)
Definition OOMesh.m:2022
NSDictionary * dictionaryRepresentation()
Definition Octree.m:667
NSDictionary * materialDefaults()
NSString * stringFromFilesNamed:inFolder:cache:(NSString *fileName,[inFolder] NSString *folderName,[cache] BOOL useCache)
voidpf void uLong size
Definition ioapi.h:134
const char * filename
Definition ioapi.h:133
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
const char int mode
Definition ioapi.h:133
Vector tangent
Definition OOMesh.h:71
GLuint vertex[3]
Definition OOMesh.h:68
Vector normal
Definition OOMesh.h:70
GLfloat t[3]
Definition OOMesh.h:73
GLfloat s[3]
Definition OOMesh.h:72
uint16_t internCount
Definition OOMesh.m:119
uint16_t internFaces[kVertexFaceDefInternalCount]
Definition OOMesh.m:120
NSMutableArray * extra
Definition OOMesh.m:121