805 lines
27 KiB
Plaintext
805 lines
27 KiB
Plaintext
|
//
|
||
|
// MyDocument.m
|
||
|
// DisplayLinkAsyncMoviePlayer
|
||
|
//
|
||
|
// Created by vade on 10/26/10.
|
||
|
// Copyright __MyCompanyName__ 2010 . All rights reserved.
|
||
|
//
|
||
|
|
||
|
#import "aiConfig.h"
|
||
|
#import "MyDocument.h"
|
||
|
#import <OpenGL/CGLMacro.h>
|
||
|
|
||
|
#pragma mark -
|
||
|
#pragma mark Helper Functions
|
||
|
|
||
|
#define aisgl_min(x,y) (x<y?x:y)
|
||
|
#define aisgl_max(x,y) (y>x?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
|