Line data Source code
1 0 : /*
2 :
3 : OOMesh.m
4 :
5 : A note on memory management:
6 : The dynamically-sized buffers used by OOMesh (_vertex etc) are the byte arrays
7 : of NSDatas, which are tracked using the _retainedObjects dictionary. This
8 : simplifies the implementation of -dealloc, but more importantly, it means
9 : bytes are refcounted. This means bytes read from the cache don't need to be
10 : copied, we just need to retain the relevant NSData object (by sticking it in
11 : _retainedObjects).
12 :
13 : Since _retainedObjects is a dictionary its members can be replaced,
14 : potentially allowing mutable meshes, although we have no use for this at
15 : present.
16 :
17 :
18 : Oolite
19 : Copyright (C) 2004-2013 Giles C Williams and contributors
20 :
21 : This program is free software; you can redistribute it and/or
22 : modify it under the terms of the GNU General Public License
23 : as published by the Free Software Foundation; either version 2
24 : of the License, or (at your option) any later version.
25 :
26 : This program is distributed in the hope that it will be useful,
27 : but WITHOUT ANY WARRANTY; without even the implied warranty of
28 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 : GNU General Public License for more details.
30 :
31 : You should have received a copy of the GNU General Public License
32 : along with this program; if not, write to the Free Software
33 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
34 : MA 02110-1301, USA.
35 :
36 : */
37 :
38 : #import "OOMesh.h"
39 : #import "Universe.h"
40 : #import "OOMeshToOctreeConverter.h"
41 : #import "ResourceManager.h"
42 : #import "Entity.h" // for NO_DRAW_DISTANCE_FACTOR.
43 : #import "Octree.h"
44 : #import "OOMaterialConvenienceCreators.h"
45 : #import "OOBasicMaterial.h"
46 : #import "OOCollectionExtractors.h"
47 : #import "OOOpenGLExtensionManager.h"
48 : #import "OOGraphicsResetManager.h"
49 : #import "OODebugGLDrawing.h"
50 : #import "OOShaderMaterial.h"
51 : #import "OOMacroOpenGL.h"
52 : #import "OOProfilingStopwatch.h"
53 : #import "OODebugFlags.h"
54 : #import "NSObjectOOExtensions.h"
55 :
56 : #import "OOJavaScriptEngine.h"
57 : #import "OODebugStandards.h"
58 :
59 : // If set, collision octree depth varies depending on the size of the mesh.
60 0 : #define ADAPTIVE_OCTREE_DEPTH 1
61 :
62 : // If set, cachable memory is scribbled with FEEDFACE to identify junk in cache.
63 0 : #define SCRIBBLE 0
64 :
65 :
66 0 : enum
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
72 : kOctreeSmallSizeThreshold = 20
73 : };
74 :
75 :
76 0 : typedef enum
77 : {
78 : kNormalModePerFace,
79 : kNormalModeSmooth,
80 : kNormalModeExplicit
81 : } OOMeshNormalMode;
82 :
83 :
84 0 : static NSString * const kOOLogMeshDataNotFound = @"mesh.load.failed.fileNotFound";
85 0 : static 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)
90 : static 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 0 : #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 : */
108 0 : enum
109 : {
110 : #if OOLITE_64_BIT
111 : kVertexFaceDefInternalCount = 11 // sizeof (VertexFaceRef) = 32
112 : #else
113 : kVertexFaceDefInternalCount = 5 // sizeof (VertexFaceRef) = 16
114 : #endif
115 : };
116 :
117 0 : typedef struct VertexFaceRef
118 : {
119 0 : uint16_t internCount;
120 0 : uint16_t internFaces[kVertexFaceDefInternalCount];
121 0 : NSMutableArray *extra;
122 0 : } VertexFaceRef;
123 :
124 :
125 0 : static void VFRAddFace(VertexFaceRef *vfr, NSUInteger index);
126 0 : static NSUInteger VFRGetCount(VertexFaceRef *vfr);
127 0 : static NSUInteger VFRGetFaceAtIndex(VertexFaceRef *vfr, NSUInteger index);
128 :
129 :
130 : @interface OOMesh (Private) <NSMutableCopying, OOGraphicsResetClient>
131 :
132 0 : - (id)initWithName:(NSString *)name
133 : cacheKey:(NSString *)cacheKey
134 : materialDictionary:(NSDictionary *)materialDict
135 : shadersDictionary:(NSDictionary *)shadersDict
136 : smooth:(BOOL)smooth
137 : shaderMacros:(NSDictionary *)macros
138 : shaderBindingTarget:(id<OOWeakReferenceSupport>)object
139 : scaleFactor:(float)scale
140 : cacheWriteable:(BOOL)cacheWriteable;
141 :
142 0 : - (BOOL) loadData:(NSString *)filename scaleFactor:(float)scale;
143 0 : - (void) checkNormalsAndAdjustWinding;
144 0 : - (void) generateFaceTangents;
145 0 : - (void) calculateVertexNormalsAndTangentsWithFaceRefs:(VertexFaceRef *)faceRefs;
146 0 : - (void) calculateVertexTangentsWithFaceRefs:(VertexFaceRef *)faceRefs;
147 :
148 0 : - (void) deleteDisplayLists;
149 :
150 0 : - (NSDictionary*) modelData;
151 0 : - (BOOL) setModelFromModelData:(NSDictionary*) dict name:(NSString *)fileName;
152 :
153 0 : - (void) getNormal:(Vector *)outNormal andTangent:(Vector *)outTangent forVertex:(OOMeshVertexCount)v_index inSmoothGroup:(OOMeshSmoothGroup)smoothGroup;
154 :
155 0 : - (BOOL) setUpVertexArrays;
156 :
157 0 : - (void) calculateBoundingVolumes;
158 :
159 0 : - (void) rescaleByFactor:(GLfloat)factor;
160 :
161 : #ifndef NDEBUG
162 0 : - (void)debugDrawNormals;
163 : #endif
164 :
165 : // Manage set of objects we need to hang on to, particularly NSDatas owning buffers.
166 0 : - (void) setRetainedObject:(id)object forKey:(NSString *)key;
167 0 : - (void *) allocateBytesWithSize:(size_t)size count:(NSUInteger)count key:(NSString *)key;
168 :
169 : // Allocate all per-vertex/per-face buffers.
170 0 : - (BOOL) allocateVertexBuffersWithCount:(NSUInteger)count;
171 0 : - (BOOL) allocateNormalBuffersWithCount:(NSUInteger)count;
172 0 : - (BOOL) allocateFaceBuffersWithCount:(NSUInteger)count;
173 0 : - (BOOL) allocateVertexArrayBuffersWithCount:(NSUInteger)count;
174 :
175 0 : - (void) renameTexturesFrom:(NSString *)from to:(NSString *)to;
176 :
177 : @end
178 :
179 :
180 : @interface OOCacheManager (OOMesh)
181 :
182 0 : + (NSDictionary *)meshDataForName:(NSString *)inShipName;
183 0 : + (void)setMeshData:(NSDictionary *)inData forName:(NSString *)inShipName;
184 :
185 : @end
186 :
187 :
188 0 : static BOOL IsLegacyNormalMode(OOMeshNormalMode mode)
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 : {
195 : case kNormalModePerFace:
196 : case kNormalModeSmooth:
197 : return YES;
198 :
199 : case kNormalModeExplicit:
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 :
210 0 : static BOOL IsPerVertexNormalMode(OOMeshNormalMode mode)
211 : {
212 : /* True for modes that have per-vertex normals, i.e. not per-face mode.
213 : */
214 : switch (mode)
215 : {
216 : case kNormalModePerFace:
217 : return NO;
218 :
219 : case kNormalModeSmooth:
220 : case kNormalModeExplicit:
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 0 : - (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 0 : - (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 :
323 : [[OOGraphicsResetManager sharedManager] unregisterClient:self];
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 :
341 0 : static 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 0 : - (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 0 : - (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 0 : - (void)renderOpaqueParts
386 : {
387 : OO_ENTER_OPENGL();
388 :
389 : BOOL meshBelongsToVisualEffect = [_shaderBindingTarget isVisualEffect];
390 :
391 : OOSetOpenGLState(OPENGL_STATE_OPAQUE);
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;
414 : OOMeshMaterialIndex ti;
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 :
532 : [OOMaterial applyNone];
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 :
563 : OOVerifyOpenGLState();
564 : }
565 :
566 :
567 : - (void) rebindMaterials
568 : {
569 : OOMeshMaterialCount i;
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 0 : - (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 0 : - (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 :
653 : if (size < kOctreeSmallSizeThreshold) result = kSmallOctreeDepth;
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 :
681 : OOMeshToOctreeConverter *converter = [OOMeshToOctreeConverter converterWithCapacity:faceCount];
682 : OOMeshFaceCount i;
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 :
742 : OOMeshVertexCount i;
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 :
776 : OOMeshVertexCount i;
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 0 : - (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 0 : - (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 0 : - (NSSet *) allTextures
824 : {
825 : NSMutableSet *result = [NSMutableSet set];
826 : OOMeshMaterialCount i;
827 : for (i = 0; i != materialCount; i++)
828 : {
829 : [result unionSet:[materials[i] allTextures]];
830 : }
831 :
832 : return result;
833 : }
834 :
835 :
836 0 : - (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 :
846 : OOMeshMaterialCount i;
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 0 : - (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
875 : materialDictionary:(NSDictionary *)materialDict
876 : shadersDictionary:(NSDictionary *)shadersDict
877 : smooth:(BOOL)smooth
878 : shaderMacros:(NSDictionary *)macros
879 : shaderBindingTarget:(id<OOWeakReferenceSupport>)target
880 : scaleFactor:(float)scale
881 : cacheWriteable:(BOOL)cacheWriteable
882 : {
883 : OOJS_PROFILE_ENTER
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 :
917 : [[OOGraphicsResetManager sharedManager] registerClient:self];
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 :
937 : OOJS_PROFILE_EXIT
938 : }
939 :
940 :
941 0 : - (id)mutableCopyWithZone:(NSZone *)zone
942 : {
943 : OOMesh *result = nil;
944 : OOMeshMaterialCount i;
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 :
969 : [[OOGraphicsResetManager sharedManager] registerClient:result];
970 : }
971 :
972 : return result;
973 : }
974 :
975 :
976 : - (void) deleteDisplayLists
977 : {
978 : if (listsReady)
979 : {
980 : OO_ENTER_OPENGL();
981 :
982 : OOGL(glDeleteLists(displayList0, materialCount));
983 : listsReady = NO;
984 : }
985 : }
986 :
987 :
988 0 : - (void) resetGraphicsState
989 : {
990 : [self deleteDisplayLists];
991 : [self rebindMaterials];
992 : _textureUnitCount = NSNotFound;
993 : }
994 :
995 :
996 : - (NSDictionary *)modelData
997 : {
998 : OOJS_PROFILE_ENTER
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 :
1065 : OOJS_PROFILE_EXIT
1066 : }
1067 :
1068 :
1069 : - (BOOL)setModelFromModelData:(NSDictionary *)dict name:(NSString *)fileName
1070 : {
1071 : OOJS_PROFILE_ENTER
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 :
1158 : OOJS_PROFILE_EXIT
1159 : }
1160 :
1161 :
1162 : - (BOOL)loadData:(NSString *)filename scaleFactor:(float)scale
1163 : {
1164 : OOJS_PROFILE_ENTER
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 :
1643 : OOJS_PROFILE_EXIT
1644 : }
1645 :
1646 :
1647 : - (void) checkNormalsAndAdjustWinding
1648 : {
1649 : OOJS_PROFILE_ENTER
1650 :
1651 : Vector calculatedNormal;
1652 : OOMeshFaceCount i;
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 :
1693 : OOJS_PROFILE_EXIT_VOID
1694 : }
1695 :
1696 :
1697 : - (void) generateFaceTangents
1698 : {
1699 : OOJS_PROFILE_ENTER
1700 :
1701 : OOMeshFaceCount i;
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 :
1735 : OOJS_PROFILE_EXIT_VOID
1736 : }
1737 :
1738 :
1739 0 : static 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 : {
1751 : OOJS_PROFILE_ENTER
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 :
1788 : OOJS_PROFILE_EXIT_VOID
1789 : }
1790 :
1791 :
1792 0 : static 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 : {
1810 : OOJS_PROFILE_ENTER
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 :
1853 : OOJS_PROFILE_EXIT_VOID
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 : {
1860 : OOJS_PROFILE_ENTER
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 :
1883 : OOJS_PROFILE_EXIT_VOID
1884 : }
1885 :
1886 :
1887 : - (BOOL) setUpVertexArrays
1888 : {
1889 : OOJS_PROFILE_ENTER
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 :
1978 : OOJS_PROFILE_EXIT
1979 : }
1980 :
1981 :
1982 : - (void) calculateBoundingVolumes
1983 : {
1984 : OOJS_PROFILE_ENTER
1985 :
1986 : OOMeshVertexCount i;
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 :
2018 : OOJS_PROFILE_EXIT_VOID
2019 : }
2020 :
2021 :
2022 : - (void) rescaleByFactor:(GLfloat)factor
2023 : {
2024 : // Rescale base vertices used for geometry calculations.
2025 : OOMeshVertexCount i;
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 0 : - (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 :
2063 : OO_ENTER_OPENGL();
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
2121 : static 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 0 : #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 : */
2200 : OOMeshMaterialCount i;
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 :
2214 0 : static 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 :
2235 0 : static 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 :
2266 0 : static 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 :
2282 0 : static NSUInteger VFRGetCount(VertexFaceRef *vfr)
2283 : {
2284 : NSCParameterAssert(vfr != NULL);
2285 :
2286 : return vfr->internCount + [vfr->extra count];
2287 : }
2288 :
2289 :
2290 0 : static 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
|