oolite/Entity.m
Dylan Smith 8b456c000b oolite initial import
git-svn-id: http://svn.berlios.de/svnroot/repos/oolite-linux/trunk@1 127b21dd-08f5-0310-b4b7-95ae10353056
2005-04-19 19:53:18 +00:00

1747 lines
49 KiB
Objective-C

//
// Entity.m
/*
*
* Oolite
*
* Created by Giles Williams on Sat Apr 03 2004.
* Copyright (c) 2004 for aegidian.org. All rights reserved.
*
Copyright (c) 2004, Giles C Williams
All rights reserved.
This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike License.
To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/2.0/
or send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
You are free:
• to copy, distribute, display, and perform the work
• to make derivative works
Under the following conditions:
• Attribution. You must give the original author credit.
• Noncommercial. You may not use this work for commercial purposes.
• Share Alike. If you alter, transform, or build upon this work,
you may distribute the resulting work only under a license identical to this one.
For any reuse or distribution, you must make clear to others the license terms of this work.
Any of these conditions can be waived if you get permission from the copyright holder.
Your fair use and other rights are in no way affected by the above.
*/
#import "Entity.h"
#import "vector.h"
#import "Universe.h"
#import "TextureStore.h"
#import "ResourceManager.h"
// global flag for VAR
BOOL global_usingVAR;
BOOL global_testForVAR;
static Universe *data_store_universe;
@implementation Entity
// class methods, they set the underlying data_storage universe
+ (void) setDataStore:(Universe *)univ
{
if (univ)
data_store_universe = univ;
//NSLog(@"--- Universe for Data Storage set to %@", univ);
global_usingVAR = NO;
global_testForVAR = YES;
}
+ (Universe *) dataStore
{
return data_store_universe;
}
- (id) init
{
self = [super init];
//
quaternion_set_identity(&q_rotation);
quaternion_into_gl_matrix(q_rotation, rotMatrix);
//
position = make_vector( 0.0, 0.0, 0.0);
//
zero_distance = 0.0; // 10 km
no_draw_distance = 100000.0; // 10 km
//
distance_travelled = 0.0;
//
energy = 0.0;
//
collision_radius = 0.0;
//
collidingEntities = [[NSMutableArray alloc] initWithCapacity:16]; // alloc automatically retains
//
scan_class = CLASS_NOT_SET;
//
universal_id = NO_TARGET;
universe = nil;
//
is_smooth_shaded = NO;
//
n_vertices = 0;
n_faces = 0;
//
displayListName = 0;
//
status = STATUS_DEMO;
//
basefile = @"No Model";
//
throw_sparks = NO;
//
usingVAR = NO;
//
return self;
}
- (void) dealloc
{
if (universe) [universe release];
if (basefile) [basefile release];
if (collidingEntities) [collidingEntities release];
[super dealloc];
}
- (void) warnAboutHostiles
{
// do nothing for now, this can be expanded in sub classes
NSLog(@"***** Entity does nothing in warnAboutHostiles");
}
- (Universe *) universe
{
return universe;
}
- (void) setUniverse:(Universe *)univ
{
if (univ)
{
if (universe) [universe release];
universe = [univ retain];
}
else
{
if (universe) [universe release];
universe = nil;
}
}
- (void) setUniversal_id:(int)uid
{
universal_id = uid;
}
- (int) universal_id
{
return universal_id;
}
- (BOOL) throwingSparks
{
return throw_sparks;
}
- (void) setThrowSparks:(BOOL) value
{
throw_sparks = value;
}
- (void) throwSparks;
{
// do nothing for now
}
- (BOOL) isSmoothShaded
{
return is_smooth_shaded;
}
- (void) setSmoothShaded:(BOOL) value
{
is_smooth_shaded = value;
}
- (void) setOwner:(Entity *) ent
{
int owner_id = [ent universal_id];
if (universe)
{
if ([universe entityForUniversalID:owner_id] == ent) // check to make sure it's kosher
owner = owner_id;
else
owner = NO_TARGET;
}
else
{
owner = owner_id; // if the universe hasn't been initialised yet, trust the sender
}
}
- (Entity *) owner
{
return [universe entityForUniversalID:owner];
}
- (void) setModel:(NSString *) modelName
{
// clear old data
if (basefile) [basefile release];
basefile = [modelName retain];
//
[self regenerateDisplayList];
//
[self loadData:basefile];
//
[self checkNormalsAndAdjustWinding];
//
if (is_smooth_shaded)
[self calculateVertexNormals];
// set the collision radius
//
collision_radius = [self findCollisionRadius];
//NSLog(@"Entity with model '%@' collision radius set to %f",modelName, collision_radius);
//
}
- (NSString *) getModel
{
return basefile;
}
- (void) setPosition:(Vector) posn
{
position.x = posn.x;
position.y = posn.y;
position.z = posn.z;
}
- (void) setPosition:(GLfloat) x:(GLfloat) y:(GLfloat) z
{
position.x = x;
position.y = y;
position.z = z;
}
- (double) getZeroDistance
{
// NSLog(@"DEBUG %@ %.1f", self, zero_distance);
return zero_distance;
}
- (Vector) relative_position
{
return relative_position;
}
- (NSComparisonResult) compareZeroDistance:(Entity *)otherEntity;
{
if (zero_distance > [otherEntity getZeroDistance])
return NSOrderedAscending;
else
return NSOrderedDescending;
}
- (BoundingBox) getBoundingBox
{
return boundingBox;
}
- (GLfloat) mass
{
return mass;
}
- (void) setQRotation:(Quaternion) quat
{
q_rotation = quat;
quaternion_into_gl_matrix(q_rotation, rotMatrix);
}
- (Quaternion) QRotation
{
return q_rotation;
}
- (void) setVelocity:(Vector) vel
{
velocity = vel;
}
- (Vector) getVelocity
{
return velocity;
}
- (double) getVelocityAsSpeed
{
return sqrt(velocity.x * velocity.x + velocity.y * velocity.y + velocity.z * velocity.z);
}
- (double) distance_travelled
{
return distance_travelled;
}
- (void) setDistanceTravelled: (double) value
{
distance_travelled = value;
}
- (void) setStatus:(int) stat
{
status = stat;
}
- (int) getStatus
{
return status;
}
- (void) setScanClass:(int) s_class
{
scan_class = s_class;
}
- (int) scanClass
{
return scan_class;
}
- (void) setEnergy:(double) amount
{
energy = amount;
}
- (double) getEnergy
{
return energy;
}
- (void) applyRoll:(GLfloat) roll andClimb:(GLfloat) climb
{
quaternion_rotate_about_z( &q_rotation, -roll);
quaternion_rotate_about_x( &q_rotation, -climb);
quaternion_normalise(&q_rotation);
quaternion_into_gl_matrix(q_rotation, rotMatrix);
}
- (void) applyRoll:(GLfloat) roll climb:(GLfloat) climb andYaw:(GLfloat) yaw
{
quaternion_rotate_about_z( &q_rotation, -roll);
quaternion_rotate_about_x( &q_rotation, -climb);
quaternion_rotate_about_y( &q_rotation, -yaw);
quaternion_normalise(&q_rotation);
quaternion_into_gl_matrix(q_rotation, rotMatrix);
}
- (void) moveForward:(double) amount
{
Vector forward = vector_forward_from_quaternion(q_rotation);
distance_travelled += amount;
position.x += amount * forward.x;
position.y += amount * forward.y;
position.z += amount * forward.z;
}
- (GLfloat *) rotationMatrix
{
return rotMatrix;
}
- (Vector) getPosition
{
return position;
}
- (Vector) getViewpointPosition
{
return position;
}
- (BOOL) canCollide
{
return YES;
}
- (double) collisionRadius
{
return collision_radius;
}
- (void) setCollisionRadius:(double) amount
{
collision_radius = amount;
}
- (NSMutableArray *) collisionArray
{
return collidingEntities;
}
- (void) drawEntity:(BOOL) immediate :(BOOL) translucent
{
// roll out each face and vertex in turn
//
// int fi,vi;
int ti;
GLfloat mat_ambient[] = { 1.0, 1.0, 1.0, 1.0 };
if (is_smooth_shaded)
glShadeModel(GL_SMOOTH);
else
glShadeModel(GL_FLAT);
//
if (!translucent)
{
if (basefile)
{
if (immediate)
{
#ifdef GNUSTEP
// TODO: Find out what these APPLE functions can be replaced with
#else
if (usingVAR)
glBindVertexArrayAPPLE(gVertexArrayRangeObjects[0]);
#endif
// if (usingVAR)
// NSLog(@"DEBUG using accelerated memory technique to draw %@ (%@)", self, basefile);
//
//
// experimental gap removal (draws flat polys)
//
glDepthMask(GL_FALSE); // don't write to depth buffer
GLfloat amb_diff0[] = { 0.5, 0.5, 0.5, 1.0};
glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, amb_diff0);
glColor4f( 0.25, 0.25, 0.25, 1.0); // gray
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_INDEX_ARRAY);
glDisableClientState(GL_EDGE_FLAG_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer( 3, GL_FLOAT, 0, entityData.vertex_array);
glEnableClientState(GL_NORMAL_ARRAY);
glNormalPointer( GL_FLOAT, 0, entityData.normal_array);
glDrawArrays( GL_TRIANGLES, 0, entityData.n_triangles);
glDepthMask(GL_TRUE);
//
// now the textures ...
//
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer( 2, GL_FLOAT, 0, entityData.texture_uv_array);
glEnable(GL_TEXTURE_2D);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
for (ti = 1; ti <= n_textures; ti++)
{
glBindTexture(GL_TEXTURE_2D, texture_name[ti]);
glDrawArrays( GL_TRIANGLES, triangle_range[ti].location, triangle_range[ti].length);
}
glDisable(GL_TEXTURE_2D);
}
else
{
if (displayListName != 0)
glCallList(displayListName);
else
{
[self initialiseTextures];
[self generateDisplayList];
}
}
}
else
{
NSLog(@"ERROR no basefile for entity %@");
NSBeep();
}
}
glShadeModel(GL_SMOOTH);
}
- (void) drawSubEntity:(BOOL) immediate :(BOOL) translucent
{
if ((owner != NO_TARGET)&&(universe))
{
// this test provides an opportunity to do simple LoD culling
//
zero_distance = [[universe entityForUniversalID:owner] getZeroDistance];
if (zero_distance > no_draw_distance)
{
//NSLog(@"DEBUG - sub entity '%@' too far away to draw", self);
return; // TOO FAR AWAY
}
}
if (status != STATUS_ACTIVE)
{
if ((![universe reducedDetail])||(status == STATUS_EFFECT)) // don't draw passive subentities except exhausts in reduced detail mode.
{
glPushMatrix();
// position and orientation is relative to owner
//NSLog(@"DEBUG drawing passive subentity at %.3f, %.3f, %.3f", position.x, position.y, position.z);
glTranslated( position.x, position.y, position.z);
glMultMatrixf(rotMatrix);
[self drawEntity:immediate :translucent];
glPopMatrix();
// NSLog(@"drawn static entity : %@", basefile);
}
}
else
{
Vector abspos; // STATUS_ACTIVE means it is in control of it's own orientation
abspos = position;
Entity* father = [self owner];
GLfloat* r_mat = [father rotationMatrix];
while (father)
{
mult_vector_gl_matrix(&abspos, r_mat);
Vector pos = [father getPosition];
abspos.x += pos.x; abspos.y += pos.y; abspos.z += pos.z;
father = [father owner];
r_mat = [father rotationMatrix];
}
glPopMatrix(); // one down
glPushMatrix();
// position and orientation is absolute
glTranslated( abspos.x, abspos.y, abspos.z);
glMultMatrixf(rotMatrix);
[self drawEntity:immediate :translucent];
// NSLog(@"drawn active entity : %@", basefile);
}
}
- (void) initialiseTextures
{
// roll out each face and tetxure in turn
//
int fi,ti ;
//
for (fi = 0; fi < n_faces; fi++)
{
// texture
if ((faces[fi].texName == 0)&&(faces[fi].textureFile))
{
// load texture into Universe texturestore
//NSLog(@"Off to load %@",faces[fi].textureFile);
if (universe)
{
faces[fi].texName = [[universe textureStore] getTextureNameFor:faces[fi].textureFile];
}
}
}
for (ti = 1; ti <= n_textures; ti++)
{
if (!texture_name[ti])
{
texture_name[ti] = [[universe textureStore] getTextureNameFor:texture_file[ti]];
// NSLog(@"DEBUG (initialiseTextures) Processed textureFile : %@ to texName : %d", entityData[ti].textureFile, entityData[ti].texName);
}
}
}
- (void) regenerateDisplayList
{
glDeleteLists(displayListName,1);
displayListName = 0;
}
- (void) generateDisplayList
{
displayListName = glGenLists(1);
if (displayListName != 0)
{
glNewList(displayListName, GL_COMPILE);
[self drawEntity:YES:NO];
glEndList();
}
}
- (void) update:(double) delta_t
{
Entity* player = [universe entityZero];
if (player)
{
Vector p0 = [player getPosition];
relative_position = make_vector( position.x - p0.x, position.y - p0.y, position.z - p0.z);
zero_distance = magnitude2(relative_position);
}
else
zero_distance = -1;
}
- (void) saveToLastFrame
{
double t_now = [universe getTime];
if (t_now >= track_time + 0.1) // update every 1/10 of a second
{
// save previous data
track_time = t_now;
track[track_index].position = position;
track[track_index].q_rotation = q_rotation;
track[track_index].timeframe = track_time;
track_index = (track_index + 1 ) & 0xff;
// if ([self isKindOfClass:[PlayerEntity class]])
// NSLog(@"Saving frame %d %.2f", track_index, track_time);
}
}
- (BOOL) resetToTime:(double) t_frame // timeframe is relative to now ie. -0.5 = half a second ago.
{
if (t_frame >= 0)
return NO;
Frame selectedFrame = [self frameAtTime:t_frame];
[self setPosition:selectedFrame.position];
[self setQRotation:selectedFrame.q_rotation];
return YES;
}
- (Frame) frameAtTime:(double) t_frame // t_frame is relative to now ie. -0.5 = half a second ago.
{
Frame result;
result.position = position;
result.q_rotation = q_rotation;
result.timeframe = [universe getTime];
//
if (t_frame >= 0.0)
return result;
//
double moment_in_time = [universe getTime] + t_frame;
if (moment_in_time >= track_time) // between the last saved frame and now
{
int t1 = (track_index - 1)&0xff; // last saved moment
double period = result.timeframe - track_time;
double f0 = (result.timeframe - moment_in_time)/period;
double f1 = 1 - f0;
Vector posn;
posn.x = f0 * result.position.x + f1 * track[t1].position.x;
posn.y = f0 * result.position.y + f1 * track[t1].position.y;
posn.z = f0 * result.position.z + f1 * track[t1].position.z;
Quaternion qrot;
qrot.w = f0 * result.q_rotation.w + f1 * track[t1].q_rotation.w;
qrot.x = f0 * result.q_rotation.x + f1 * track[t1].q_rotation.x;
qrot.y = f0 * result.q_rotation.y + f1 * track[t1].q_rotation.y;
qrot.z = f0 * result.q_rotation.z + f1 * track[t1].q_rotation.z;
result.position = posn;
result.q_rotation = qrot;
result.timeframe = moment_in_time;
return result;
}
//
if (moment_in_time < track[track_index].timeframe) // more than 256 frames back
{
return track[track_index];
}
//
int t1 = (track_index - 1)&0xff;
while (moment_in_time < track[t1].timeframe)
t1 = (t1 - 1) & 0xff;
int t0 = (t1 + 1) & 0xff;
// interpolate between t0 and t1
double period = track[0].timeframe - track[1].timeframe;
double f0 = (track[t0].timeframe - moment_in_time)/period;
double f1 = 1 - f0;
Vector posn;
posn.x = f0 * track[t0].position.x + f1 * track[t1].position.x;
posn.y = f0 * track[t0].position.y + f1 * track[t1].position.y;
posn.z = f0 * track[t0].position.z + f1 * track[t1].position.z;
Quaternion qrot;
qrot.w = f0 * track[t0].q_rotation.w + f1 * track[t1].q_rotation.w;
qrot.x = f0 * track[t0].q_rotation.x + f1 * track[t1].q_rotation.x;
qrot.y = f0 * track[t0].q_rotation.y + f1 * track[t1].q_rotation.y;
qrot.z = f0 * track[t0].q_rotation.z + f1 * track[t1].q_rotation.z;
result.position = posn;
result.q_rotation = qrot;
result.timeframe = moment_in_time;
return result;
}
- (void) loadData:(NSString *) filename
{
NSScanner *scanner;
NSString *data = nil;
NSMutableArray *lines;
BOOL failFlag = NO;
NSString *failString = @"***** ";
int i, j;
BOOL using_preloaded = NO;
if (data_store_universe)
{
if ([[data_store_universe preloadedDataFiles] objectForKey:filename])
{
// NSLog(@"Reusing data for %@ from [data_store_universe preloadedDataFiles]", filename);
data = (NSString *)[[data_store_universe preloadedDataFiles] objectForKey:filename];
using_preloaded = YES;
}
else
{
data = [ResourceManager stringFromFilesNamed:filename inFolder:@"Models"];
if (data != nil)
[[data_store_universe preloadedDataFiles] setObject:data forKey:filename];
}
}
// failsafe in case the stored data fails
if (data == nil)
{
data = [ResourceManager stringFromFilesNamed:filename inFolder:@"Models"];
using_preloaded = NO;
}
// strip out comments and commas between values
//
lines = [NSMutableArray arrayWithArray:[data componentsSeparatedByString:@"\n"]];
for (i = 0; i < [ lines count]; i++)
{
NSString *line = [lines objectAtIndex:i];
NSArray *parts;
//
// comments
//
parts = [line componentsSeparatedByString:@"#"];
line = [parts objectAtIndex:0];
parts = [line componentsSeparatedByString:@"//"];
line = [parts objectAtIndex:0];
//
// commas
//
line = [[line componentsSeparatedByString:@","] componentsJoinedByString:@" "];
//
[lines replaceObjectAtIndex:i withObject:line];
}
data = [lines componentsJoinedByString:@"\n"];
//NSLog(@"More data:\n%@",data);
scanner = [NSScanner scannerWithString:data];
// get number of vertices
//
[scanner setScanLocation:0]; //reset
//[scanner scanCharactersFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet] intoString:nil];
if ([scanner scanString:@"NVERTS" intoString:nil])
{
int n_v;
if ([scanner scanInt:&n_v])
n_vertices = n_v;
else
{
failFlag = YES;
failString = [NSString stringWithFormat:@"%@Failed to read value of NVERTS\n",failString];
}
}
else
{
failFlag = YES;
failString = [NSString stringWithFormat:@"%@Failed to read NVERTS\n",failString];
}
// get number of faces
//
//[scanner setScanLocation:0]; //reset
if ([scanner scanString:@"NFACES" intoString:nil])
{
int n_f;
if ([scanner scanInt:&n_f])
n_faces = n_f;
else
{
failFlag = YES;
failString = [NSString stringWithFormat:@"%@Failed to read value of NFACES\n",failString];
}
}
else
{
failFlag = YES;
failString = [NSString stringWithFormat:@"%@Failed to read NFACES\n",failString];
}
// get vertex data
//
//[scanner setScanLocation:0]; //reset
if ([scanner scanString:@"VERTEX" intoString:nil])
{
for (j = 0; j < n_vertices; j++)
{
float x, y, z;
if (!failFlag)
{
if (![scanner scanFloat:&x])
failFlag = YES;
if (![scanner scanFloat:&y])
failFlag = YES;
if (![scanner scanFloat:&z])
failFlag = YES;
if (!failFlag)
{
vertices[j].x = x; vertices[j].y = y; vertices[j].z = z;
}
else
{
failString = [NSString stringWithFormat:@"%@Failed to read a value for vertex[%d] in VERTEX\n", failString, j];
}
}
}
}
else
{
failFlag = YES;
failString = [NSString stringWithFormat:@"%@Failed to find VERTEX data\n",failString];
}
// get face data
//
if ([scanner scanString:@"FACES" intoString:nil])
{
for (j = 0; j < n_faces; j++)
{
int r, g, b;
float nx, ny, nz;
int n_v;
if (!failFlag)
{
// colors
//
if (![scanner scanInt:&r])
failFlag = YES;
if (![scanner scanInt:&g])
failFlag = YES;
if (![scanner scanInt:&b])
failFlag = YES;
if (!failFlag)
{
faces[j].red = r/255.0; faces[j].green = g/255.0; faces[j].blue = b/255.0;
}
else
{
failString = [NSString stringWithFormat:@"%@Failed to read a color for face[%d] in FACES\n", failString, j];
}
// normal
//
if (![scanner scanFloat:&nx])
failFlag = YES;
if (![scanner scanFloat:&ny])
failFlag = YES;
if (![scanner scanFloat:&nz])
failFlag = YES;
if (!failFlag)
{
faces[j].normal.x = nx; faces[j].normal.y = ny; faces[j].normal.z = nz;
}
else
{
failString = [NSString stringWithFormat:@"%@Failed to read a normal for face[%d] in FACES\n", failString, j];
}
// vertices
//
if ([scanner scanInt:&n_v])
{
faces[j].n_verts = n_v;
}
else
{
failFlag = YES;
failString = [NSString stringWithFormat:@"%@Failed to read number of vertices for face[%d] in FACES\n", failString, j];
}
//
if (!failFlag)
{
int vi;
for (i = 0; i < n_v; i++)
{
if ([scanner scanInt:&vi])
{
faces[j].vertex[i] = vi;
}
else
{
failFlag = YES;
failString = [NSString stringWithFormat:@"%@Failed to read vertex[%d] for face[%d] in FACES\n", failString, i, j];
}
}
}
}
}
}
else
{
failFlag = YES;
failString = [NSString stringWithFormat:@"%@Failed to find FACES data\n",failString];
}
NSMutableDictionary* facesForTexture = [NSMutableDictionary dictionaryWithCapacity:MAX_TEXTURES_PER_ENTITY];
// get textures data
//
if ([scanner scanString:@"TEXTURES" intoString:nil])
{
for (j = 0; j < n_faces; j++)
{
NSString *texfile;
float max_x, max_y;
float s, t;
if (!failFlag)
{
// texfile
//
[scanner scanCharactersFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet] intoString:nil];
if (![scanner scanUpToCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString:&texfile])
{
failFlag = YES;
failString = [NSString stringWithFormat:@"%@Failed to read texture filename for face[%d] in TEXTURES\n", failString, j];
}
else
{
faces[j].textureFile = [texfile retain];
// create/extend a list of faces for this texture
NSMutableArray* facesForThisTexture;
if ([facesForTexture objectForKey:texfile])
facesForThisTexture = (NSMutableArray*)[facesForTexture objectForKey:texfile];
else
facesForThisTexture = [NSMutableArray arrayWithCapacity:32];
[facesForThisTexture addObject:[NSNumber numberWithInt:j]];
[facesForTexture setObject:facesForThisTexture forKey:texfile];
}
faces[j].texName = 0;
// texture size
//
if (!failFlag)
{
if (![scanner scanFloat:&max_x])
failFlag = YES;
if (![scanner scanFloat:&max_y])
failFlag = YES;
if (failFlag)
failString = [NSString stringWithFormat:@"%@Failed to read texture size for max_x and max_y in face[%d] in TEXTURES\n", failString, j];
}
// vertices
//
if (!failFlag)
{
for (i = 0; i < faces[j].n_verts; i++)
{
if (![scanner scanFloat:&s])
failFlag = YES;
if (![scanner scanFloat:&t])
failFlag = YES;
if (!failFlag)
{
faces[j].s[i] = s / max_x; faces[j].t[i] = t / max_y;
//NSLog(@" st %f %f", faces[j].s[i], faces[j].t[i]);
}
else
failString = [NSString stringWithFormat:@"%@Failed to read s t coordinates for vertex[%d] in face[%d] in TEXTURES\n", failString, i, j];
}
}
}
}
}
else
{
failFlag = YES;
failString = [NSString stringWithFormat:@"%@Failed to find TEXTURES data\n",failString];
}
// NSLog(@"Loading data for %@: facesForTexture:\n%@", filename, [facesForTexture description]);
// check normals before creating new textures
//
[self checkNormalsAndAdjustWinding];
if ((failFlag)&&([failString rangeOfString:@"TEXTURES"].location != NSNotFound))
{
//NSLog(@"Off to make new textures!");
[self fakeTexturesWithImageFile:@"metal.png" andMaxSize:NSMakeSize(256.0,256.0)];
// dump out data for ships with faked textures
//if ([self isKindOfClass:[ShipEntity class]])
// //NSLog(@"Faked Texture coordinates for this model :\n\n%@\n\n", [self toString]);
}
if (failFlag)
NSLog([NSString stringWithFormat:@"%@ ..... from %@ %@", failString, filename, (using_preloaded)? @"(from preloaded data)" : @"(from file)"]);
// set the collision radius
//
collision_radius = [self findCollisionRadius];
//
[self setUpVertexArrays];
//
//
usingVAR = [self OGL_InitVAR];
//
if (usingVAR)
{
[self OGL_AssignVARMemory:sizeof(EntityData) :(void *)&entityData :0];
}
//
}
- (void) checkNormalsAndAdjustWinding
{
Vector calculatedNormal;
int i, j;
for (i = 0; i < n_faces; i++)
{
Vector v0, v1, v2, norm;
v0 = vertices[faces[i].vertex[0]];
v1 = vertices[faces[i].vertex[1]];
v2 = vertices[faces[i].vertex[2]];
norm = faces[i].normal;
calculatedNormal = normal_to_surface (v2, v1, v0);
if ((norm.x == 0.0)&&(norm.y == 0.0)&&(norm.z == 0.0))
{
//NSLog(@"Using calculated normal for face %d", i);
faces[i].normal = normal_to_surface (v0, v1, v2);
norm = normal_to_surface (v0, v1, v2);
}
if ((norm.x*calculatedNormal.x < 0)||(norm.y*calculatedNormal.y < 0)||(norm.z*calculatedNormal.z < 0))
{
// normal lies in the WRONG direction!
// reverse the winding
int v[faces[i].n_verts];
GLfloat s[faces[i].n_verts];
GLfloat t[faces[i].n_verts];
//
//NSLog(@"Normal pointing the wrong way for winding on face %d", i);
//
for (j = 0; j < faces[i].n_verts; j++)
{
v[j] = faces[i].vertex[faces[i].n_verts - 1 - j];
s[j] = faces[i].s[faces[i].n_verts - 1 - j];
t[j] = faces[i].t[faces[i].n_verts - 1 - j];
}
for (j = 0; j < faces[i].n_verts; j++)
{
faces[i].vertex[j] = v[j];
faces[i].s[j] = s[j];
faces[i].t[j] = t[j];
}
}
}
}
- (void) calculateVertexNormals
{
int i,j,k;
for (i = 0; i < n_vertices; i++)
{
int shared_faces = 0;
Vector normal_sum;
normal_sum.x = 0.0; normal_sum.y = 0.0; normal_sum.z = 0.0;
for (j = 0; j < n_faces; j++)
{
BOOL is_shared = NO;
for (k = 0; (k < faces[j].n_verts)&&(!is_shared); k++)
is_shared = (faces[j].vertex[k] == i);
if (is_shared)
{
normal_sum.x += faces[j].normal.x; normal_sum.y += faces[j].normal.y; normal_sum.z += faces[j].normal.z;
shared_faces++;
}
}
normal_sum = unit_vector(&normal_sum);
vertex_normal[i].x = normal_sum.x;
vertex_normal[i].y = normal_sum.y;
vertex_normal[i].z = normal_sum.z;
}
}
- (void) setUpVertexArrays
{
NSMutableDictionary* texturesProcessed = [NSMutableDictionary dictionaryWithCapacity:MAX_TEXTURES_PER_ENTITY];
int face, fi, vi, texi;
// base model, flat shaded, all triangles
int tri_index = 0;
int uv_index = 0; // not used
int vertex_index = 0;
int normal_index = 0;
entityData.textureFile = nil;
entityData.texName = 0;
texi = 1; // index of first texture
for (face = 0; face < n_faces; face++)
{
NSString* tex_string = faces[face].textureFile;
if (![texturesProcessed objectForKey:tex_string])
{
// do this texture
triangle_range[texi].location = tri_index;
texture_file[texi] = tex_string;
texture_name[texi] = faces[face].texName;
for (fi = 0; fi < n_faces; fi++)
{
Vector normal;
int v;
if (!is_smooth_shaded)
normal = faces[fi].normal;
if ([faces[fi].textureFile isEqual:tex_string])
{
for (vi = 0; vi < 3; vi++)
{
v = faces[fi].vertex[vi];
if (is_smooth_shaded)
normal = vertex_normal[v];
entityData.index_array[tri_index++] = vertex_index;
entityData.vertex_array[vertex_index++] = vertices[v];
entityData.normal_array[normal_index++] = normal;
entityData.texture_uv_array[uv_index++] = faces[fi].s[vi];
entityData.texture_uv_array[uv_index++] = faces[fi].t[vi];
}
}
}
triangle_range[texi].length = tri_index - triangle_range[texi].location;
// NSLog(@"DEBUG processing %@ texture %@ texName %d triangles %d to %d",
// basefile, texture_file[texi], texture_name[texi], triangle_range[texi].location, triangle_range[texi].location + triangle_range[texi].length);
//finally...
[texturesProcessed setObject:tex_string forKey:tex_string]; // note this texture done
texi++;
}
}
entityData.n_triangles = tri_index; // total number of triangle vertices
triangle_range[0] = NSMakeRange( 0, tri_index);
n_textures = texi - 1;
}
- (double) findCollisionRadius
{
int i;
double d_squared, result, length_longest_axis, length_shortest_axis;
result = 0.0;
bounding_box_reset(&boundingBox);
for (i = 0; i < n_vertices; i++)
{
d_squared = vertices[i].x*vertices[i].x + vertices[i].y*vertices[i].y + vertices[i].z*vertices[i].z;
if (d_squared > result)
result = d_squared;
bounding_box_add_vector(&boundingBox,vertices[i]);
}
length_longest_axis = boundingBox.max_x - boundingBox.min_x;
if (boundingBox.max_y - boundingBox.min_y > length_longest_axis)
length_longest_axis = boundingBox.max_y - boundingBox.min_y;
if (boundingBox.max_z - boundingBox.min_z > length_longest_axis)
length_longest_axis = boundingBox.max_z - boundingBox.min_z;
length_shortest_axis = boundingBox.max_x - boundingBox.min_x;
if (boundingBox.max_y - boundingBox.min_y < length_shortest_axis)
length_shortest_axis = boundingBox.max_y - boundingBox.min_y;
if (boundingBox.max_z - boundingBox.min_z < length_shortest_axis)
length_shortest_axis = boundingBox.max_z - boundingBox.min_z;
d_squared = (length_longest_axis + length_shortest_axis) * (length_longest_axis + length_shortest_axis) * 0.25; // square of average length
no_draw_distance = d_squared * NO_DRAW_DISTANCE_FACTOR * NO_DRAW_DISTANCE_FACTOR; // no longer based on the collision radius
mass = (boundingBox.max_x - boundingBox.min_x) * (boundingBox.max_y - boundingBox.min_y) * (boundingBox.max_z - boundingBox.min_z);
// NSLog(@"%@ has mass %.3f", basefile, mass);
return sqrt(result);
}
- (BoundingBox) findBoundingBoxRelativeTo:(Entity *)other InVectors:(Vector) _i :(Vector) _j :(Vector) _k
{
Vector pv, rv;
Vector rpos = position;
Vector opv = [other getPosition];
rpos.x -= opv.x; rpos.y -= opv.y; rpos.z -= opv.z;
rv.x = dot_product(_i,rpos);
rv.y = dot_product(_j,rpos);
rv.z = dot_product(_k,rpos);
BoundingBox result;
bounding_box_reset_to_vector(&result,rv);
int i;
for (i = 0; i < n_vertices; i++)
{
pv.x = rpos.x + vertices[i].x;
pv.y = rpos.y + vertices[i].y;
pv.z = rpos.z + vertices[i].z;
rv.x = dot_product(_i,pv);
rv.y = dot_product(_j,pv);
rv.z = dot_product(_k,pv);
bounding_box_add_vector(&result,rv);
}
return result;
}
- (BoundingBox) findBoundingBoxRelativeToPosition:(Vector)opv InVectors:(Vector) _i :(Vector) _j :(Vector) _k
{
Vector pv, rv;
Vector rpos = position;
rpos.x -= opv.x; rpos.y -= opv.y; rpos.z -= opv.z;
rv.x = dot_product(_i,rpos);
rv.y = dot_product(_j,rpos);
rv.z = dot_product(_k,rpos);
BoundingBox result;
bounding_box_reset_to_vector(&result,rv);
int i;
for (i = 0; i < n_vertices; i++)
{
pv.x = rpos.x + vertices[i].x;
pv.y = rpos.y + vertices[i].y;
pv.z = rpos.z + vertices[i].z;
rv.x = dot_product(_i,pv);
rv.y = dot_product(_j,pv);
rv.z = dot_product(_k,pv);
bounding_box_add_vector(&result,rv);
}
return result;
}
- (BOOL) checkCloseCollisionWith:(Entity *)other
{
return YES;
}
- (void) takeEnergyDamage:(double) amount from:(Entity *) ent becauseOf:(Entity *) other
{
}
- (NSString *) toString
{
// produce a file from the original data
int i,j, r,g,b;
NSString *result;
NSString *boilerplate = @"# This is a file adapted from the model files for Java Elite\n# which in turn are based on the data released by Ian Bell\n# in the file b7051600.zip at\n# http://www.users.waitrose.com/~elitearc2/elite/archive/b7051600.zip\n#";
result = [NSString stringWithFormat:@"%@\n# %@\n#\n\nNVERTS %d\nNFACES %d\n\nVERTEX\n", boilerplate, basefile, n_vertices, n_faces];
for (i = 0; i < n_vertices; i++)
{
result = [NSString stringWithFormat:@"%@%f,\t%f,\t%f\n", result, vertices[i].x, vertices[i].y, vertices[i].z];
if ((i % 5)==4)
result = [NSString stringWithFormat:@"%@\n", result];
}
result = [NSString stringWithFormat:@"%@\nFACES\n", result];
//
//NSLog(result);
//
for (j = 0; j < n_faces; j++)
{
r = (int)(faces[j].red * 255.0); g = (int)(faces[j].green * 255.0); b = (int)(faces[j].blue * 255.0);
result = [NSString stringWithFormat:@"%@%d, %d, %d,\t", result, r, g, b];
result = [NSString stringWithFormat:@"%@%f, %f, %f,\t", result, faces[j].normal.x, faces[j].normal.y, faces[j].normal.z];
result = [NSString stringWithFormat:@"%@%d,\t", result, faces[j].n_verts];
for (i = 0; i < faces[j].n_verts; i++)
{
result = [NSString stringWithFormat:@"%@%d ", result, faces[j].vertex[i]];
}
result = [NSString stringWithFormat:@"%@\n", result];
}
if (universe)
{
result = [NSString stringWithFormat:@"%@\nTEXTURES\n", result];
for (j = 0; j < n_faces; j++)
{
NSSize texSize = [[universe textureStore] getSizeOfTexture:faces[j].textureFile];
result = [NSString stringWithFormat:@"%@%@\t%d %d", result, faces[j].textureFile, (int)texSize.width, (int)texSize.height];
for (i = 0; i < faces[j].n_verts; i++)
{
int s = (int)(faces[j].s[i] * texSize.width);
int t = (int)(faces[j].t[i] * texSize.height);
result = [NSString stringWithFormat:@"%@\t%d %d", result, s, t];
}
result = [NSString stringWithFormat:@"%@\n", result];
}
}
result = [NSString stringWithFormat:@"%@\nEND\n", result];
return result;
}
- (void) fakeTexturesWithImageFile: (NSString *) textureFile andMaxSize:(NSSize) maxSize
{
int i, j, k;
Vector vec;
int nf = 0;
int fi[MAX_FACES_PER_ENTITY];
float max_s, min_s, max_t, min_t, st_width, st_height;
float tolerance;
Face fa[MAX_FACES_PER_ENTITY];
int faces_to_match;
BOOL face_matched[MAX_FACES_PER_ENTITY];
tolerance = 1.00;
faces_to_match = n_faces;
for (i = 0; i < n_faces; i++)
{
face_matched[i] = NO;
}
while (faces_to_match > 0)
{
tolerance -= 0.05;
// Top (+y) first
vec.x = 0.0; vec.y = 1.0; vec.z = 0.0;
// build list of faces that face in that direction...
nf = 0;
max_s = -999999.0; min_s = 999999.0;
max_t = -999999.0; min_t = 999999.0;
for (i = 0; i < n_faces; i++)
{
float s, t;
float g = dot_product(vec, faces[i].normal) * sqrt(2.0);
if ((g >= tolerance)&&(!face_matched[i]))
{
fi[nf++] = i;
face_matched[i] = YES;
faces_to_match--;
for (j = 0; j < faces[i].n_verts; j++)
{
s = vertices[faces[i].vertex[j]].x;
t = vertices[faces[i].vertex[j]].z;
max_s = (max_s > s) ? max_s:s ; min_s = (min_s < s) ? min_s:s ;
max_t = (max_t > t) ? max_t:t ; min_t = (min_t < t) ? min_t:t ;
}
}
}
//
st_width = max_s - min_s;
st_height = max_t - min_t;
//
//NSLog(@"TOP st_width %f st_height %f maxSize.height %f maxSize.width %f", st_width, st_height, maxSize.width, maxSize.height);
//
for (j = 0; j < nf; j++)
{
i = fi[j];
//fa[i] = faces[i];
fa[i].textureFile = [NSString stringWithFormat:@"top_%@", textureFile];
for (k = 0; k < faces[i].n_verts; k++)
{
float s, t;
s = vertices[faces[i].vertex[k]].x;
t = vertices[faces[i].vertex[k]].z;
fa[i].s[k] = (s - min_s) * maxSize.width / st_width;
fa[i].t[k] = (t - min_t) * maxSize.height / st_height;
//
// TESTING
//
fa[i].t[k] = maxSize.height - fa[i].t[k]; // REVERSE t locations
//
//NSLog(@"%f, %f", fa[i].s[k], fa[i].t[k]);
//
}
}
// Bottom (-y)
vec.x = 0.0; vec.y = -1.0; vec.z = 0.0;
// build list of faces that face in that direction...
nf = 0;
max_s = -999999.0; min_s = 999999.0;
max_t = -999999.0; min_t = 999999.0;
for (i = 0; i < n_faces; i++)
{
float s, t;
float g = dot_product(vec, faces[i].normal) * sqrt(2.0);
if ((g >= tolerance)&&(!face_matched[i]))
{
fi[nf++] = i;
face_matched[i] = YES;
faces_to_match--;
for (j = 0; j < faces[i].n_verts; j++)
{
s = -vertices[faces[i].vertex[j]].x;
t = -vertices[faces[i].vertex[j]].z;
max_s = (max_s > s) ? max_s:s ; min_s = (min_s < s) ? min_s:s ;
max_t = (max_t > t) ? max_t:t ; min_t = (min_t < t) ? min_t:t ;
}
}
}
st_width = max_s - min_s;
st_height = max_t - min_t;
for (j = 0; j < nf; j++)
{
i = fi[j];
//fa[i] = faces[i];
fa[i].textureFile = [NSString stringWithFormat:@"bottom_%@", textureFile];
for (k = 0; k < faces[i].n_verts; k++)
{
float s, t;
s = -vertices[faces[i].vertex[k]].x;
t = -vertices[faces[i].vertex[k]].z;
fa[i].s[k] = (s - min_s) * maxSize.width / st_width;
fa[i].t[k] = (t - min_t) * maxSize.height / st_height;
}
}
// Right (+x)
vec.x = 1.0; vec.y = 0.0; vec.z = 0.0;
// build list of faces that face in that direction...
nf = 0;
max_s = -999999.0; min_s = 999999.0;
max_t = -999999.0; min_t = 999999.0;
for (i = 0; i < n_faces; i++)
{
float s, t;
float g = dot_product(vec, faces[i].normal) * sqrt(2.0);
if ((g >= tolerance)&&(!face_matched[i]))
{
fi[nf++] = i;
face_matched[i] = YES;
faces_to_match--;
for (j = 0; j < faces[i].n_verts; j++)
{
s = vertices[faces[i].vertex[j]].z;
t = vertices[faces[i].vertex[j]].y;
max_s = (max_s > s) ? max_s:s ; min_s = (min_s < s) ? min_s:s ;
max_t = (max_t > t) ? max_t:t ; min_t = (min_t < t) ? min_t:t ;
}
}
}
st_width = max_s - min_s;
st_height = max_t - min_t;
for (j = 0; j < nf; j++)
{
i = fi[j];
//fa[i] = faces[i];
fa[i].textureFile = [NSString stringWithFormat:@"right_%@", textureFile];
for (k = 0; k < faces[i].n_verts; k++)
{
float s, t;
s = vertices[faces[i].vertex[k]].z;
t = vertices[faces[i].vertex[k]].y;
fa[i].s[k] = (s - min_s) * maxSize.width / st_width;
fa[i].t[k] = (t - min_t) * maxSize.height / st_height;
}
}
// Left (-x)
vec.x = -1.0; vec.y = 0.0; vec.z = 0.0;
// build list of faces that face in that direction...
nf = 0;
max_s = -999999.0; min_s = 999999.0;
max_t = -999999.0; min_t = 999999.0;
for (i = 0; i < n_faces; i++)
{
float s, t;
float g = dot_product(vec, faces[i].normal) * sqrt(2.0);
if ((g >= tolerance)&&(!face_matched[i]))
{
fi[nf++] = i;
face_matched[i] = YES;
faces_to_match--;
for (j = 0; j < faces[i].n_verts; j++)
{
s = -vertices[faces[i].vertex[j]].z;
t = -vertices[faces[i].vertex[j]].y;
max_s = (max_s > s) ? max_s:s ; min_s = (min_s < s) ? min_s:s ;
max_t = (max_t > t) ? max_t:t ; min_t = (min_t < t) ? min_t:t ;
}
}
}
st_width = max_s - min_s;
st_height = max_t - min_t;
for (j = 0; j < nf; j++)
{
i = fi[j];
//fa[i] = faces[i];
fa[i].textureFile = [NSString stringWithFormat:@"left_%@", textureFile];
for (k = 0; k < faces[i].n_verts; k++)
{
float s, t;
s = -vertices[faces[i].vertex[k]].z;
t = -vertices[faces[i].vertex[k]].y;
fa[i].s[k] = (s - min_s) * maxSize.width / st_width;
fa[i].t[k] = (t - min_t) * maxSize.height / st_height;
}
}
// Front (+z)
vec.x = 0.0; vec.y = 0.0; vec.z = 1.0;
// build list of faces that face in that direction...
nf = 0;
max_s = -999999.0; min_s = 999999.0;
max_t = -999999.0; min_t = 999999.0;
for (i = 0; i < n_faces; i++)
{
float s, t;
float g = dot_product(vec, faces[i].normal) * sqrt(2.0);
if ((g >= tolerance)&&(!face_matched[i]))
{
fi[nf++] = i;
face_matched[i] = YES;
faces_to_match--;
for (j = 0; j < faces[i].n_verts; j++)
{
s = vertices[faces[i].vertex[j]].x;
t = vertices[faces[i].vertex[j]].y;
max_s = (max_s > s) ? max_s:s ; min_s = (min_s < s) ? min_s:s ;
max_t = (max_t > t) ? max_t:t ; min_t = (min_t < t) ? min_t:t ;
}
}
}
st_width = max_s - min_s;
st_height = max_t - min_t;
for (j = 0; j < nf; j++)
{
i = fi[j];
//fa[i] = faces[i];
fa[i].textureFile = [NSString stringWithFormat:@"front_%@", textureFile];
for (k = 0; k < faces[i].n_verts; k++)
{
float s, t;
s = vertices[faces[i].vertex[k]].x;
t = vertices[faces[i].vertex[k]].y;
fa[i].s[k] = (s - min_s) * maxSize.width / st_width;
fa[i].t[k] = (t - min_t) * maxSize.height / st_height;
}
}
// Back (-z)
vec.x = 0.0; vec.y = 0.0; vec.z = -1.0;
// build list of faces that face in that direction...
nf = 0;
max_s = -999999.0; min_s = 999999.0;
max_t = -999999.0; min_t = 999999.0;
for (i = 0; i < n_faces; i++)
{
float s, t;
float g = dot_product(vec, faces[i].normal) * sqrt(2.0);
if ((g >= tolerance)&&(!face_matched[i]))
{
fi[nf++] = i;
face_matched[i] = YES;
faces_to_match--;
for (j = 0; j < faces[i].n_verts; j++)
{
s = -vertices[faces[i].vertex[j]].x;
t = -vertices[faces[i].vertex[j]].y;
max_s = (max_s > s) ? max_s:s ; min_s = (min_s < s) ? min_s:s ;
max_t = (max_t > t) ? max_t:t ; min_t = (min_t < t) ? min_t:t ;
}
}
}
st_width = max_s - min_s;
st_height = max_t - min_t;
for (j = 0; j < nf; j++)
{
i = fi[j];
//fa[i] = faces[i];
fa[i].textureFile = [NSString stringWithFormat:@"back_%@", textureFile];
for (k = 0; k < faces[i].n_verts; k++)
{
float s, t;
s = -vertices[faces[i].vertex[k]].x;
t = -vertices[faces[i].vertex[k]].y;
fa[i].s[k] = (s - min_s) * maxSize.width / st_width;
fa[i].t[k] = (t - min_t) * maxSize.height / st_height;
}
}
//NSLog(@"%d / %d faces matched at tolerance: %f", n_faces - faces_to_match, n_faces, tolerance);
}
for (i = 0; i < n_faces; i++)
{
NSString *result;
faces[i].textureFile = [fa[i].textureFile retain];
faces[i].texName = 0;
for (j = 0; j < faces[i].n_verts; j++)
{
//
//NSLog(@"face[%d] %f, %f", i, fa[i].s[j], fa[i].t[j]);
//
faces[i].s[j] = fa[i].s[j] / maxSize.width;
faces[i].t[j] = fa[i].t[j] / maxSize.height;
}
result = [NSString stringWithFormat:@"%@\t%d %d", faces[i].textureFile, (int)maxSize.width, (int)maxSize.height];
//NSLog(@"face[%d] : %@", i, result);
}
}
// COMMON OGL STUFF
- (BOOL) OGL_InitVAR
{
short i;
static char* s;
if (global_testForVAR)
{
global_testForVAR = NO; // no need for further tests after this
// see if we have supported hardware
s = (char *)glGetString(GL_EXTENSIONS); // get extensions list
if (strstr(s, "GL_APPLE_vertex_array_range") == nil)
{
global_usingVAR &= NO;
NSLog(@"Vertex Array Range optimisation - not supported");
return NO;
}
else
{
NSLog(@"Vertex Array Range optimisation - supported");
global_usingVAR |= YES;
}
}
if (!global_usingVAR)
return NO;
#ifdef GNUSTEP
// TODO: Find out what these APPLE functions do
#else
glGenVertexArraysAPPLE(NUM_VERTEX_ARRAY_RANGES, &gVertexArrayRangeObjects[0]);
#endif
// INIT OUR DATA
//
// None of the VAR objects has been assigned to any data yet,
// so here we just initialize our info. We'll assign the VAR objects
// to data later.
//
for (i = 0; i < NUM_VERTEX_ARRAY_RANGES; i++)
{
gVertexArrayRangeData[i].rangeSize = 0;
gVertexArrayRangeData[i].dataBlockPtr = nil;
gVertexArrayRangeData[i].forceUpdate = true;
gVertexArrayRangeData[i].activated = false;
}
return YES;
}
- (void) OGL_AssignVARMemory:(long) size :(void *) data :(Byte) whichVAR
{
if (whichVAR >= NUM_VERTEX_ARRAY_RANGES)
{
NSLog(@"VAR is out of range!");
exit(-1);
}
gVertexArrayRangeData[whichVAR].rangeSize = size;
gVertexArrayRangeData[whichVAR].dataBlockPtr = data;
gVertexArrayRangeData[whichVAR].forceUpdate = true;
}
- (void) OGL_UpdateVAR
{
long size;
Byte i;
for (i = 0; i < NUM_VERTEX_ARRAY_RANGES; i++)
{
// SEE IF THIS VAR IS USED
size = gVertexArrayRangeData[i].rangeSize;
if (size == 0)
continue;
// SEE IF VAR NEEDS UPDATING
if (!gVertexArrayRangeData[i].forceUpdate)
continue;
#ifdef GNUSTEP
// TODO: find out what non-AAPL OpenGL stuff is equivalent
#else
// BIND THIS VAR OBJECT SO WE CAN DO STUFF TO IT
glBindVertexArrayAPPLE(gVertexArrayRangeObjects[i]);
// SEE IF THIS IS THE FIRST TIME IN
if (!gVertexArrayRangeData[i].activated)
{
glVertexArrayRangeAPPLE(size, gVertexArrayRangeData[i].dataBlockPtr);
glVertexArrayParameteriAPPLE(GL_VERTEX_ARRAY_STORAGE_HINT_APPLE,GL_STORAGE_SHARED_APPLE);
// you MUST call this flush to get the data primed!
glFlushVertexArrayRangeAPPLE(size, gVertexArrayRangeData[i].dataBlockPtr);
glEnableClientState(GL_VERTEX_ARRAY_RANGE_APPLE);
gVertexArrayRangeData[i].activated = true;
}
// ALREADY ACTIVE, SO JUST UPDATING
else
{
glFlushVertexArrayRangeAPPLE(size, gVertexArrayRangeData[i].dataBlockPtr);
}
#endif
gVertexArrayRangeData[i].forceUpdate = false;
}
}
@end