#import "cimport.h"
#import "config.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 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)
{
}
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];
CGLContextObj cgl_ctx = (CGLContextObj)[_glContext CGLContextObj];
glEnable(GL_MULTISAMPLE);
glClearColor(0.3, 0.3, 0.3, 0.3);
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);
glEnableClientState(GL_VERTEX_ARRAY);
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];
if([[NSFileManager defaultManager] fileExistsAtPath:[openPanel filename]])
{
aiPropertyStore* props = aiCreatePropertyStore();
aiSetImportPropertyInteger(props, 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;
}
_scene = (aiScene*) aiImportFileExWithProperties([[openPanel filename] cStringUsingEncoding:[NSString defaultCStringEncoding]], aiPostProccesFlags | aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_PreTransformVertices | 0, NULL, props);
aiReleasePropertyStore(props);
if (_scene)
{
textureDictionary = [[NSMutableDictionary alloc] initWithCapacity:5];
CGLContextObj cgl_ctx = (CGLContextObj)[_glContext CGLContextObj];
CGLLockContext(cgl_ctx);
[self loadTexturesInContext:cgl_ctx withModelPath:[[openPanel filename] stringByStandardizingPath]];
[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;
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);
}
}
}
}]; }
- (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);
if(_scene)
{
glScaled(normalizedScale , normalizedScale, normalizedScale);
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
- (void) createGLResourcesInContext:(CGLContextObj)cgl_ctx
{
modelMeshes = [[NSMutableArray alloc] initWithCapacity:_scene->mNumMeshes];
for (unsigned int i = 0; i < _scene->mNumMeshes; ++i)
{
NSLog(@"%u", i);
const aiMesh* mesh = _scene->mMeshes[i];
MeshHelper* meshHelper = [[MeshHelper alloc] init];
aiMaterial* mtl = _scene->mMaterials[mesh->mMaterialIndex];
int texIndex = 0;
aiString texPath;
if(AI_SUCCESS == mtl->GetTexture(aiTextureType_DIFFUSE, texIndex, &texPath))
{
NSString* textureKey = [NSString stringWithCString:texPath.data encoding:[NSString defaultCStringEncoding]];
NSNumber* textureNumber = (NSNumber*)[textureDictionary valueForKey:textureKey];
meshHelper.textureID = [textureNumber unsignedIntValue];
}
else
meshHelper.textureID = 0;
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];
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];
GLuint vhandle;
glGenBuffers(1, &vhandle);
glBindBuffer(GL_ARRAY_BUFFER, vhandle);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * mesh->mNumVertices, NULL, GL_STATIC_DRAW);
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);
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);
{
memset( verts->mBoneIndices, 0, sizeof( verts->mBoneIndices));
memset( verts->mBoneWeights, 0, sizeof( verts->mBoneWeights));
}
++verts;
}
glUnmapBufferARB(GL_ARRAY_BUFFER_ARB); glBindBuffer(GL_ARRAY_BUFFER, 0);
meshHelper.vertexBuffer = vhandle;
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);
}
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);
for (unsigned int x = 0; x < mesh->mNumFaces; ++x)
{
for (unsigned int a = 0; a < nidx; ++a)
{
*indices++ = mesh->mFaces[x].mIndices[a];
}
}
glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
meshHelper.indexBuffer = ihandle;
meshHelper.numIndices = mesh->mNumFaces * nidx;
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));
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(Vertex), 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, meshHelper.indexBuffer);
glBindVertexArrayAPPLE(0);
meshHelper.vao = vaoHandle;
GLuint list = glGenLists(1);
glNewList(list, GL_COMPILE);
float dc[4];
float sc[4];
float ac[4];
float emc[4];
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);
if(meshHelper.twoSided)
glEnable(GL_CULL_FACE);
else
glDisable(GL_CULL_FACE);
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);
glBindVertexArrayAPPLE(meshHelper.vao);
glDrawElements(GL_TRIANGLES, meshHelper.numIndices, GL_UNSIGNED_INT, 0);
glEndList();
meshHelper.displayList = list;
[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)
{
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;
}
for (unsigned int m = 0; m < _scene->mNumMaterials; m++)
{
int texIndex = 0;
aiReturn texFound = AI_SUCCESS;
aiString path;
while (texFound == AI_SUCCESS)
{
texFound = _scene->mMaterials[m]->GetTexture(aiTextureType_DIFFUSE, texIndex, &path);
NSString* texturePath = [NSString stringWithCString:path.data encoding:[NSString defaultCStringEncoding]];
[textureDictionary setValue:[NSNumber numberWithUnsignedInt:texIndex] forKey:texturePath];
texIndex++;
}
}
textureIds = (GLuint*) malloc(sizeof(GLuint) * [textureDictionary count]); glGenTextures([textureDictionary count], textureIds);
NSLog(@"textureDictionary: %@", textureDictionary);
NSDictionary *textureCopy = [textureDictionary copy];
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)
{
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]);
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);
int samplesPerPixel = [bitmap samplesPerPixel];
if(![bitmap isPlanar] && (samplesPerPixel == 3 || samplesPerPixel == 4))
{
glTexImage2D(GL_TEXTURE_2D,
0,
samplesPerPixel == 4 ? GL_RGBA8 : GL_RGB8,
[bitmap pixelsWide],
[bitmap pixelsHigh],
0,
samplesPerPixel == 4 ? GL_RGBA : GL_RGB,
GL_UNSIGNED_BYTE,
[bitmap bitmapData]);
}
[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:(aiVector3D*) min maxVectr:(aiVector3D*) max
{
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 aiNode*)nd minVector:(aiVector3D*) min maxVector:(aiVector3D*) max matrix:(aiMatrix4x4*) trafo
{
aiMatrix4x4 prev;
unsigned int n = 0, t;
prev = *trafo;
aiMultiplyMatrix4(trafo,&nd->mTransformation);
for (; n < nd->mNumMeshes; ++n)
{
const aiMesh* mesh = _scene->mMeshes[nd->mMeshes[n]];
for (t = 0; t < mesh->mNumVertices; ++t)
{
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