// // MyDocument.m // DisplayLinkAsyncMoviePlayer // // Created by vade on 10/26/10. // Copyright __MyCompanyName__ 2010 . All rights reserved. // #import "aiConfig.h" #import "MyDocument.h" #import #pragma mark - #pragma mark Helper Functions #define aisgl_min(x,y) (xx?y:x) static void color4_to_float4(const struct aiColor4D *c, float f[4]) { f[0] = c->r; f[1] = c->g; f[2] = c->b; f[3] = c->a; } static void set_float4(float f[4], float a, float b, float c, float d) { f[0] = a; f[1] = b; f[2] = c; f[3] = d; } #pragma mark - #pragma mark CVDisplayLink Callback static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink,const CVTimeStamp *inNow,const CVTimeStamp *inOutputTime,CVOptionFlags flagsIn,CVOptionFlags *flagsOut,void *displayLinkContext) { CVReturn error = [(MyDocument*) displayLinkContext displayLinkRenderCallback:inOutputTime]; return error; } #pragma mark - @implementation MyDocument @synthesize _view; - (id)init { self = [super init]; if (self != nil) { // initialization code } return self; } - (NSString *)windowNibName { return @"MyDocument"; } - (void)windowControllerDidLoadNib:(NSWindowController *)windowController { [super windowControllerDidLoadNib:windowController]; NSOpenGLPixelFormatAttribute attributes[] = { NSOpenGLPFADoubleBuffer, NSOpenGLPFAAccelerated, NSOpenGLPFADepthSize, 24, NSOpenGLPFAMultisample, NSOpenGLPFASampleBuffers, 2, (NSOpenGLPixelFormatAttribute) 0 }; _glPixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; if(!_glPixelFormat) NSLog(@"Error creating PF"); _glContext = [[NSOpenGLContext alloc] initWithFormat:_glPixelFormat shareContext:nil]; const GLint one = 1; [_glContext setValues:&one forParameter:NSOpenGLCPSwapInterval]; [_glContext setView:_view]; // Set up initial GL state. CGLContextObj cgl_ctx = (CGLContextObj)[_glContext CGLContextObj]; glEnable(GL_MULTISAMPLE); glClearColor(0.3, 0.3, 0.3, 0.3); // enable color tracking //glEnable(GL_COLOR_MATERIAL); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glDepthMask(GL_TRUE); glEnable(GL_NORMALIZE); glEnable(GL_TEXTURE_2D); glShadeModel(GL_SMOOTH); glEnable(GL_LIGHTING); GLfloat global_ambient[] = { 0.5f, 0.5f, 0.5f, 1.0f }; glLightModelfv(GL_LIGHT_MODEL_AMBIENT, global_ambient); GLfloat specular[] = {1.0f, 1.0f, 1.0f, 1.0f}; glLightfv(GL_LIGHT0, GL_SPECULAR, specular); GLfloat diffuse[] = {1.0f, 1.0f, 1.0f, 1.0f}; glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse); GLfloat ambient[] = {0.2, 0.2f, 0.2f, 0.2f}; glLightfv(GL_LIGHT0, GL_AMBIENT, ambient); GLfloat position[] = { 1.0f, 1.0f, 1.0f, 1.0f}; glLightfv(GL_LIGHT0, GL_POSITION, position); glEnable(GL_LIGHT0); // This is the only client state that always has to be set. glEnableClientState(GL_VERTEX_ARRAY); // end GL State setup. // Display Link setup. CVReturn error = kCVReturnSuccess; error = CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink); if(error == kCVReturnError) NSLog(@"Error Creating DisplayLink"); error = CVDisplayLinkSetOutputCallback(_displayLink,MyDisplayLinkCallback, self); if(error == kCVReturnError) NSLog(@"Error Setting DisplayLink Callback"); error = CVDisplayLinkStart(_displayLink); if(error == kCVReturnError) NSLog(@"Error Starting DisplayLink"); NSOpenPanel* openPanel = [NSOpenPanel openPanel]; [openPanel beginSheetModalForWindow:[_view window] completionHandler:^(NSInteger result) { if (result == NSOKButton) { [openPanel orderOut:self]; // close panel before we might present an error if([[NSFileManager defaultManager] fileExistsAtPath:[openPanel filename]]) { // Load our new path. // only ever give us triangles. aiSetImportPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT ); NSUInteger aiPostProccesFlags; switch (2) { case 0: aiPostProccesFlags = aiProcessPreset_TargetRealtime_Fast; break; case 1: aiPostProccesFlags = aiProcessPreset_TargetRealtime_Quality; break; case 2: aiPostProccesFlags = aiProcessPreset_TargetRealtime_MaxQuality; break; default: aiPostProccesFlags = aiProcessPreset_TargetRealtime_MaxQuality; break; } // aiProcess_FlipUVs is needed for VAO / VBOs, not sure why. _scene = (aiScene*) aiImportFile([[openPanel filename] cStringUsingEncoding:[NSString defaultCStringEncoding]], aiPostProccesFlags | aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_PreTransformVertices | 0 ); if (_scene) { textureDictionary = [[NSMutableDictionary alloc] initWithCapacity:5]; // Why do I need to cast this !? CGLContextObj cgl_ctx = (CGLContextObj)[_glContext CGLContextObj]; CGLLockContext(cgl_ctx); [self loadTexturesInContext:cgl_ctx withModelPath:[[openPanel filename] stringByStandardizingPath]]; //NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:[NSValue valueWithPointer:cgl_ctx], @"context", [self.inputModelPath stringByStandardizingPath], @"path", nil ]; //[self performSelectorInBackground:@selector(loadTexturesInBackground:) withObject:userInfo]; [self getBoundingBoxWithMinVector:&scene_min maxVectr:&scene_max]; scene_center.x = (scene_min.x + scene_max.x) / 2.0f; scene_center.y = (scene_min.y + scene_max.y) / 2.0f; scene_center.z = (scene_min.z + scene_max.z) / 2.0f; // optional normalized scaling normalizedScale = scene_max.x-scene_min.x; normalizedScale = aisgl_max(scene_max.y - scene_min.y,normalizedScale); normalizedScale = aisgl_max(scene_max.z - scene_min.z,normalizedScale); normalizedScale = 1.f / normalizedScale; if(_scene->HasAnimations()) NSLog(@"scene has animations"); [self createGLResourcesInContext:cgl_ctx]; CGLUnlockContext(cgl_ctx); } } } }]; // end block handler } - (void) close { CVDisplayLinkStop(_displayLink); CVDisplayLinkRelease(_displayLink); if(_scene) { aiReleaseImport(_scene); _scene = NULL; CGLContextObj cgl_ctx = (CGLContextObj)[_glContext CGLContextObj]; glDeleteTextures([textureDictionary count], textureIds); [textureDictionary release]; textureDictionary = nil; free(textureIds); textureIds = NULL; [self deleteGLResourcesInContext:cgl_ctx]; } [_glContext release]; _glContext = nil; [_glPixelFormat release]; _glPixelFormat = nil; [super close]; } - (CVReturn)displayLinkRenderCallback:(const CVTimeStamp *)timeStamp { CVReturn rv = kCVReturnError; NSAutoreleasePool *pool; pool = [[NSAutoreleasePool alloc] init]; { [self render]; rv = kCVReturnSuccess; } [pool release]; return rv; } - (void) render { CGLContextObj cgl_ctx = (CGLContextObj)[_glContext CGLContextObj]; CGLLockContext(cgl_ctx); [_glContext update]; glMatrixMode(GL_PROJECTION); glLoadIdentity(); glViewport(0, 0, _view.frame.size.width, _view.frame.size.height); GLfloat aspect = _view.frame.size.height/_view.frame.size.width; glOrtho(-1, 1, - (aspect), aspect, -10, 10); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glTranslated(0.0, 0.0, 1.0); // Draw our GL model. if(_scene) { glScaled(normalizedScale , normalizedScale, normalizedScale); // center the model glTranslated( -scene_center.x, -scene_center.y, -scene_center.z); glScaled(2.0, 2.0, 2.0); static float i = 0; i+=0.5; glRotated(i, 0, 1, 0); [self drawMeshesInContext:cgl_ctx]; } CGLUnlockContext(cgl_ctx); CGLFlushDrawable(cgl_ctx); } #pragma mark - #pragma mark Loading // Inspired by LoadAsset() & CreateAssetData() from AssimpView D3D project - (void) createGLResourcesInContext:(CGLContextObj)cgl_ctx { // create new mesh helpers for each mesh, will populate their data later. modelMeshes = [[NSMutableArray alloc] initWithCapacity:_scene->mNumMeshes]; // create OpenGL buffers and populate them based on each meshes pertinant info. for (unsigned int i = 0; i < _scene->mNumMeshes; ++i) { NSLog(@"%u", i); // current mesh we are introspecting const aiMesh* mesh = _scene->mMeshes[i]; // the current meshHelper we will be populating data into. MeshHelper* meshHelper = [[MeshHelper alloc] init]; // Handle material info aiMaterial* mtl = _scene->mMaterials[mesh->mMaterialIndex]; // Textures int texIndex = 0; aiString texPath; if(AI_SUCCESS == mtl->GetTexture(aiTextureType_DIFFUSE, texIndex, &texPath)) { NSString* textureKey = [NSString stringWithCString:texPath.data encoding:[NSString defaultCStringEncoding]]; //bind texture NSNumber* textureNumber = (NSNumber*)[textureDictionary valueForKey:textureKey]; //NSLog(@"applyMaterialInContext: have texture %i", [textureNumber unsignedIntValue]); meshHelper.textureID = [textureNumber unsignedIntValue]; } else meshHelper.textureID = 0; // Colors aiColor4D dcolor = aiColor4D(0.8f, 0.8f, 0.8f, 1.0f); if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_DIFFUSE, &dcolor)) [meshHelper setDiffuseColor:&dcolor]; aiColor4D scolor = aiColor4D(0.0f, 0.0f, 0.0f, 1.0f); if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_SPECULAR, &scolor)) [meshHelper setSpecularColor:&scolor]; aiColor4D acolor = aiColor4D(0.2f, 0.2f, 0.2f, 1.0f); if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_AMBIENT, &acolor)) [meshHelper setAmbientColor:&acolor]; aiColor4D ecolor = aiColor4D(0.0f, 0.0f, 0.0f, 1.0f); if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_EMISSIVE, &ecolor)) [meshHelper setEmissiveColor:&ecolor]; // Culling unsigned int max = 1; int two_sided; if((AI_SUCCESS == aiGetMaterialIntegerArray(mtl, AI_MATKEY_TWOSIDED, &two_sided, &max)) && two_sided) [meshHelper setTwoSided:YES]; else [meshHelper setTwoSided:NO]; // Create a VBO for our vertices GLuint vhandle; glGenBuffers(1, &vhandle); glBindBuffer(GL_ARRAY_BUFFER, vhandle); glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * mesh->mNumVertices, NULL, GL_STATIC_DRAW); // populate vertices Vertex* verts = (Vertex*)glMapBuffer(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB); for (unsigned int x = 0; x < mesh->mNumVertices; ++x) { verts->vPosition = mesh->mVertices[x]; if (NULL == mesh->mNormals) verts->vNormal = aiVector3D(0.0f,0.0f,0.0f); else verts->vNormal = mesh->mNormals[x]; if (NULL == mesh->mTangents) { verts->vTangent = aiVector3D(0.0f,0.0f,0.0f); verts->vBitangent = aiVector3D(0.0f,0.0f,0.0f); } else { verts->vTangent = mesh->mTangents[x]; verts->vBitangent = mesh->mBitangents[x]; } if (mesh->HasVertexColors(0)) { verts->dColorDiffuse = mesh->mColors[0][x]; } else verts->dColorDiffuse = aiColor4D(1.0, 1.0, 1.0, 1.0); // This varies slightly form Assimp View, we support the 3rd texture component. if (mesh->HasTextureCoords(0)) verts->vTextureUV = mesh->mTextureCoords[0][x]; else verts->vTextureUV = aiVector3D(0.5f,0.5f, 0.0f); if (mesh->HasTextureCoords(1)) verts->vTextureUV2 = mesh->mTextureCoords[1][x]; else verts->vTextureUV2 = aiVector3D(0.5f,0.5f, 0.0f); // TODO: handle Bone indices and weights /* if( mesh->HasBones()) { unsigned char boneIndices[4] = { 0, 0, 0, 0 }; unsigned char boneWeights[4] = { 0, 0, 0, 0 }; ai_assert( weightsPerVertex[x].size() <= 4); for( unsigned int a = 0; a < weightsPerVertex[x].size(); a++) { boneIndices[a] = weightsPerVertex[x][a].mVertexId; boneWeights[a] = (unsigned char) (weightsPerVertex[x][a].mWeight * 255.0f); } memcpy( verts->mBoneIndices, boneIndices, sizeof( boneIndices)); memcpy( verts->mBoneWeights, boneWeights, sizeof( boneWeights)); } else */ { memset( verts->mBoneIndices, 0, sizeof( verts->mBoneIndices)); memset( verts->mBoneWeights, 0, sizeof( verts->mBoneWeights)); } ++verts; } glUnmapBufferARB(GL_ARRAY_BUFFER_ARB); //invalidates verts glBindBuffer(GL_ARRAY_BUFFER, 0); // set the mesh vertex buffer handle to our new vertex buffer. meshHelper.vertexBuffer = vhandle; // Create Index Buffer // populate the index buffer. NSUInteger nidx; switch (mesh->mPrimitiveTypes) { case aiPrimitiveType_POINT: nidx = 1;break; case aiPrimitiveType_LINE: nidx = 2;break; case aiPrimitiveType_TRIANGLE: nidx = 3;break; default: assert(false); } // create the index buffer GLuint ihandle; glGenBuffers(1, &ihandle); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ihandle); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * mesh->mNumFaces * nidx, NULL, GL_STATIC_DRAW); unsigned int* indices = (unsigned int*)glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY_ARB); // now fill the index buffer for (unsigned int x = 0; x < mesh->mNumFaces; ++x) { for (unsigned int a = 0; a < nidx; ++a) { // if(mesh->mFaces[x].mNumIndices != 3) // NSLog(@"whoa dont have 3 indices..."); *indices++ = mesh->mFaces[x].mIndices[a]; } } glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); // set the mesh index buffer handle to our new index buffer. meshHelper.indexBuffer = ihandle; meshHelper.numIndices = mesh->mNumFaces * nidx; // create the normal buffer. Assimp View creates a second normal buffer. Unsure why. Using only the interleaved normals for now. // This is here for reference. /* GLuint nhandle; glGenBuffers(1, &nhandle); glBindBuffer(GL_ARRAY_BUFFER, nhandle); glBufferData(GL_ARRAY_BUFFER, sizeof(aiVector3D)* mesh->mNumVertices, NULL, GL_STATIC_DRAW); // populate normals aiVector3D* normals = (aiVector3D*)glMapBuffer(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB); for (unsigned int x = 0; x < mesh->mNumVertices; ++x) { aiVector3D vNormal = mesh->mNormals[x]; *normals = vNormal; ++normals; } glUnmapBufferARB(GL_ARRAY_BUFFER_ARB); //invalidates verts glBindBuffer(GL_ARRAY_BUFFER, 0); meshHelper.normalBuffer = nhandle; */ // Create VAO and populate it GLuint vaoHandle; glGenVertexArraysAPPLE(1, &vaoHandle); glBindVertexArrayAPPLE(vaoHandle); glBindBuffer(GL_ARRAY_BUFFER, meshHelper.vertexBuffer); glEnableClientState(GL_NORMAL_ARRAY); glNormalPointer(GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(12)); glEnableClientState(GL_COLOR_ARRAY); glColorPointer(4, GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(24)); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glTexCoordPointer(3, GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(64)); //TODO: handle second texture // VertexPointer ought to come last, apparently this is some optimization, since if its set once, first, it gets fiddled with every time something else is update. glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, sizeof(Vertex), 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, meshHelper.indexBuffer); glBindVertexArrayAPPLE(0); // save the VAO handle into our mesh helper meshHelper.vao = vaoHandle; // Create the display list GLuint list = glGenLists(1); glNewList(list, GL_COMPILE); float dc[4]; float sc[4]; float ac[4]; float emc[4]; // Material colors and properties color4_to_float4([meshHelper diffuseColor], dc); glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, dc); color4_to_float4([meshHelper specularColor], sc); glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, sc); color4_to_float4([meshHelper ambientColor], ac); glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ac); color4_to_float4(meshHelper.emissiveColor, emc); glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, emc); glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); // Culling if(meshHelper.twoSided) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); // Texture Binding glBindTexture(GL_TEXTURE_2D, meshHelper.textureID); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // This binds the whole VAO, inheriting all the buffer and client state. Weeee glBindVertexArrayAPPLE(meshHelper.vao); glDrawElements(GL_TRIANGLES, meshHelper.numIndices, GL_UNSIGNED_INT, 0); glEndList(); meshHelper.displayList = list; // Whew, done. Save all of this shit. [modelMeshes addObject:meshHelper]; [meshHelper release]; } } - (void) deleteGLResourcesInContext:(CGLContextObj)cgl_ctx { for(MeshHelper* helper in modelMeshes) { const GLuint indexBuffer = helper.indexBuffer; const GLuint vertexBuffer = helper.vertexBuffer; const GLuint normalBuffer = helper.normalBuffer; const GLuint vaoHandle = helper.vao; const GLuint dlist = helper.displayList; glDeleteBuffers(1, &vertexBuffer); glDeleteBuffers(1, &indexBuffer); glDeleteBuffers(1, &normalBuffer); glDeleteVertexArraysAPPLE(1, &vaoHandle); glDeleteLists(1, dlist); helper.indexBuffer = 0; helper.vertexBuffer = 0; helper.normalBuffer = 0; helper.vao = 0; helper.displayList = 0; } [modelMeshes release]; modelMeshes = nil; } - (void) drawMeshesInContext:(CGLContextObj)cgl_ctx { for(MeshHelper* helper in modelMeshes) { // Set up meterial state. glCallList(helper.displayList); } } - (void) loadTexturesInContext:(CGLContextObj)cgl_ctx withModelPath:(NSString*) modelPath { if (_scene->HasTextures()) { NSLog(@"Support for meshes with embedded textures is not implemented"); return; } /* getTexture Filenames and Numb of Textures */ for (unsigned int m = 0; m < _scene->mNumMaterials; m++) { int texIndex = 0; aiReturn texFound = AI_SUCCESS; aiString path; // filename // TODO: handle other aiTextureTypes while (texFound == AI_SUCCESS) { texFound = _scene->mMaterials[m]->GetTexture(aiTextureType_DIFFUSE, texIndex, &path); NSString* texturePath = [NSString stringWithCString:path.data encoding:[NSString defaultCStringEncoding]]; // add our path to the texture and the index to our texture dictionary. [textureDictionary setValue:[NSNumber numberWithUnsignedInt:texIndex] forKey:texturePath]; texIndex++; } } textureIds = (GLuint*) malloc(sizeof(GLuint) * [textureDictionary count]); //new GLuint[ [textureDictionary count] ]; glGenTextures([textureDictionary count], textureIds); NSLog(@"textureDictionary: %@", textureDictionary); // create our textures, populate them, and alter our textureID value for the specific textureID we create. // so we can modify while we enumerate... NSDictionary *textureCopy = [textureDictionary copy]; // GCD attempt. //dispatch_sync(_queue, ^{ int i = 0; for(NSString* texturePath in textureCopy) { NSString* fullTexturePath = [[[modelPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:[texturePath stringByStandardizingPath]] stringByStandardizingPath]; NSLog(@"texturePath: %@", fullTexturePath); NSImage* textureImage = [[NSImage alloc] initWithContentsOfFile:fullTexturePath]; if(textureImage) { //NSLog(@"Have Texture Image"); NSBitmapImageRep* bitmap = [NSBitmapImageRep alloc]; [textureImage lockFocus]; [bitmap initWithFocusedViewRect:NSMakeRect(0, 0, textureImage.size.width, textureImage.size.height)]; [textureImage unlockFocus]; glActiveTexture(GL_TEXTURE0); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, textureIds[i]); //glPixelStorei(GL_UNPACK_ROW_LENGTH, [bitmap pixelsWide]); //glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // generate mip maps glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // draw into our bitmap int samplesPerPixel = [bitmap samplesPerPixel]; if(![bitmap isPlanar] && (samplesPerPixel == 3 || samplesPerPixel == 4)) { glTexImage2D(GL_TEXTURE_2D, 0, //samplesPerPixel == 4 ? GL_COMPRESSED_RGBA_S3TC_DXT3_EXT : GL_COMPRESSED_RGB_S3TC_DXT1_EXT, samplesPerPixel == 4 ? GL_RGBA8 : GL_RGB8, [bitmap pixelsWide], [bitmap pixelsHigh], 0, samplesPerPixel == 4 ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, [bitmap bitmapData]); } // update our dictionary to contain the proper textureID value (from out array of generated IDs) [textureDictionary setValue:[NSNumber numberWithUnsignedInt:textureIds[i]] forKey:texturePath]; [bitmap release]; } else { [textureDictionary removeObjectForKey:texturePath]; NSLog(@"Could not Load Texture: %@, removing reference to it.", fullTexturePath); } [textureImage release]; i++; } //}); [textureCopy release]; } - (void) getBoundingBoxWithMinVector:(struct aiVector3D*) min maxVectr:(struct aiVector3D*) max { struct aiMatrix4x4 trafo; aiIdentityMatrix4(&trafo); min->x = min->y = min->z = 1e10f; max->x = max->y = max->z = -1e10f; [self getBoundingBoxForNode:_scene->mRootNode minVector:min maxVector:max matrix:&trafo]; } - (void) getBoundingBoxForNode:(const struct aiNode*)nd minVector:(struct aiVector3D*) min maxVector:(struct aiVector3D*) max matrix:(struct aiMatrix4x4*) trafo { struct aiMatrix4x4 prev; unsigned int n = 0, t; prev = *trafo; aiMultiplyMatrix4(trafo,&nd->mTransformation); for (; n < nd->mNumMeshes; ++n) { const struct aiMesh* mesh = _scene->mMeshes[nd->mMeshes[n]]; for (t = 0; t < mesh->mNumVertices; ++t) { struct aiVector3D tmp = mesh->mVertices[t]; aiTransformVecByMatrix4(&tmp,trafo); min->x = aisgl_min(min->x,tmp.x); min->y = aisgl_min(min->y,tmp.y); min->z = aisgl_min(min->z,tmp.z); max->x = aisgl_max(max->x,tmp.x); max->y = aisgl_max(max->y,tmp.y); max->z = aisgl_max(max->z,tmp.z); } } for (n = 0; n < nd->mNumChildren; ++n) { [self getBoundingBoxForNode:nd->mChildren[n] minVector:min maxVector:max matrix:trafo]; } *trafo = prev; } @end