diff --git a/Images/clear256.png b/Images/clear256.png index 2725f6b..1d4f6ca 100644 Binary files a/Images/clear256.png and b/Images/clear256.png differ diff --git a/Images/clear256.pxm b/Images/clear256.pxm new file mode 100644 index 0000000..06d94ff Binary files /dev/null and b/Images/clear256.pxm differ diff --git a/Images/grid256.acorn b/Images/grid256.acorn index cabc265..b988eb9 100644 Binary files a/Images/grid256.acorn and b/Images/grid256.acorn differ diff --git a/Images/grid256.png b/Images/grid256.png index 9ccd7e5..b3fb924 100644 Binary files a/Images/grid256.png and b/Images/grid256.png differ diff --git a/Images/grid256.pxm b/Images/grid256.pxm new file mode 100644 index 0000000..49baf26 Binary files /dev/null and b/Images/grid256.pxm differ diff --git a/Source/RAGeometry.h b/Source/RAGeometry.h index 5e0742c..a0d4fbc 100644 --- a/Source/RAGeometry.h +++ b/Source/RAGeometry.h @@ -20,7 +20,8 @@ @property (assign, nonatomic) NSInteger colorOffset; // GLFloat r, g, b, a @property (assign, nonatomic) NSInteger textureOffset; // GLFloat s, t -@property (strong, nonatomic) RATextureWrapper * texture; +@property (strong, nonatomic) RATextureWrapper * texture0; +@property (strong, nonatomic) RATextureWrapper * texture1; @property (assign, nonatomic) GLKVector4 color; // set 1st component to -1 to disable @property (retain, nonatomic) GLKEffectPropertyMaterial * material; @property (assign, nonatomic) GLenum elementStyle; // default: GL_TRIANGLES diff --git a/Source/RAGeometry.m b/Source/RAGeometry.m index ed77608..753d189 100644 --- a/Source/RAGeometry.m +++ b/Source/RAGeometry.m @@ -33,13 +33,15 @@ @implementation RAGeometry { NSMutableData * _indexData; GLint _indexStride; + + BOOL _needsSetup; } @synthesize positionOffset = _positionOffset; @synthesize normalOffset = _normalOffset; @synthesize colorOffset = _colorOffset; @synthesize textureOffset = _textureOffset; -@synthesize texture = _texture; +@synthesize texture0 = _texture0, texture1 = _texture1; @synthesize color = _color; @synthesize material = _material; @synthesize elementStyle = _elementStyle; @@ -63,6 +65,8 @@ - (id)init _color = GLKVector4Make(-1, -1, -1, -1); _elementStyle = GL_TRIANGLES; + + _needsSetup = YES; } return self; } @@ -144,6 +148,8 @@ - (void)setIndexData:(const void *)data withSize:(NSUInteger)length withStride:( - (void)setupGL { + if ( _needsSetup == NO ) return; + // create vertex array if needed if ( _vertexArray == BUFFER_INVALID ) { glGenVertexArraysOES(1, &_vertexArray); @@ -184,11 +190,16 @@ - (void)setupGL glVertexAttribPointer(GLKVertexAttribColor, 4, GL_FLOAT, GL_FALSE, _vertexStride, (const GLvoid *)_colorOffset); } - if ( _textureOffset >= 0 ) { + if ( _textureOffset >= 0 && _texture0 ) { glEnableVertexAttribArray(GLKVertexAttribTexCoord0); glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, _vertexStride, (const GLvoid *)_textureOffset); } + if ( _textureOffset >= 0 && _texture1 ) { + glEnableVertexAttribArray(GLKVertexAttribTexCoord1); + glVertexAttribPointer(GLKVertexAttribTexCoord1, 2, GL_FLOAT, GL_FALSE, _vertexStride, (const GLvoid *)_textureOffset); + } + glBindVertexArrayOES(0); } @@ -201,6 +212,8 @@ - (void)releaseGL _vertexBuffer = BUFFER_INVALID; _indexBuffer = BUFFER_INVALID; _vertexArray = BUFFER_INVALID; + + _needsSetup = YES; } - (void)renderGL diff --git a/Source/RAPage.h b/Source/RAPage.h index c023c2f..83905da 100644 --- a/Source/RAPage.h +++ b/Source/RAPage.h @@ -29,8 +29,6 @@ @property (strong) RAGeometry * geometry; @property (strong) UIImage * image; -@property (weak) NSOperation * buildOp; -@property (assign) BOOL needsUpdate; @property (assign) NSTimeInterval lastRequestTime; - (RAPage *)initWithTileID:(TileID)t andParent:(RAPage *)parent; @@ -41,7 +39,4 @@ - (void)setCenter:(GLKVector3)center andRadius:(double)radius; -- (void)setupGL; -- (void)releaseGL; - @end diff --git a/Source/RAPage.m b/Source/RAPage.m index a7468c5..18b35f5 100644 --- a/Source/RAPage.m +++ b/Source/RAPage.m @@ -24,7 +24,7 @@ @implementation RAPage { @synthesize geometry = _geometry; @synthesize image = _image; @synthesize parent = _parent, child1, child2, child3, child4; -@synthesize buildOp, needsUpdate, lastRequestTime; +@synthesize lastRequestTime; - (RAPage *)initWithTileID:(TileID)t andParent:(RAPage *)parent; { @@ -65,30 +65,4 @@ - (BOOL)isLeaf { return ( child1 == nil && child2 == nil && child3 == nil && child4 == nil ); } -- (void)setupGL { - if ( self.needsUpdate == NO ) return; - - // create texture - GLKTextureInfo * textureInfo = nil; - if ( _image ) { - NSError * err = nil; - NSDictionary * options = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:GLKTextureLoaderOriginBottomLeft]; - textureInfo = [GLKTextureLoader textureWithCGImage:[_image CGImage] options:options error:&err]; - if ( err ) NSLog(@"Error loading texture: %@", err); - - self.geometry.texture = [[RATextureWrapper alloc] initWithTextureInfo:textureInfo]; - } /*else { - NSLog(@"No image available for tile %@.", key); - }*/ - - self.needsUpdate = NO; -} - -- (void)releaseGL { - [self.geometry releaseGL]; - - // release texture - self.needsUpdate = YES; -} - @end diff --git a/Source/RARenderVisitor.m b/Source/RARenderVisitor.m index bcb774f..c514e40 100644 --- a/Source/RARenderVisitor.m +++ b/Source/RARenderVisitor.m @@ -89,16 +89,24 @@ - (void)renderWithEffect:(GLKBaseEffect *)effect effect.useConstantColor = child.geometry.color.x > -1; effect.constantColor = child.geometry.color; - if (child.geometry.texture != nil) { + if (child.geometry.texture0 != nil) { effect.texture2d0.envMode = GLKTextureEnvModeModulate; effect.texture2d0.target = GLKTextureTarget2D; - effect.texture2d0.name = child.geometry.texture.name; + effect.texture2d0.name = child.geometry.texture0.name; effect.texture2d0.enabled = YES; } else { - effect.texture2d0.name = child.geometry.texture.name; effect.texture2d0.enabled = NO; } - + + if (child.geometry.texture1 != nil) { + effect.texture2d1.envMode = GLKTextureEnvModeModulate; + effect.texture2d1.target = GLKTextureTarget2D; + effect.texture2d1.name = child.geometry.texture1.name; + effect.texture2d1.enabled = YES; + } else { + effect.texture2d1.enabled = NO; + } + [effect prepareToDraw]; [child.geometry renderGL]; }]; diff --git a/Source/RASceneGraphController.m b/Source/RASceneGraphController.m index 856e1df..8ad3d93 100644 --- a/Source/RASceneGraphController.m +++ b/Source/RASceneGraphController.m @@ -115,6 +115,9 @@ - (void)viewDidLoad manipulator.view = self.view; + // create another context to load textures into + pager.loadingContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 sharegroup:[context sharegroup]]; + [self setupGL]; } diff --git a/Source/RATilePager.h b/Source/RATilePager.h index 84dbb48..f3b459c 100644 --- a/Source/RATilePager.h +++ b/Source/RATilePager.h @@ -8,7 +8,7 @@ #import -#import +#import #import "RATileDatabase.h" #import "RAGeographicUtils.h" @@ -20,6 +20,7 @@ @interface RATilePager : NSObject @property (strong) RATileDatabase * database; +@property (strong) EAGLContext * loadingContext; @property (readonly) RAGroup * nodes; @property (readonly) NSSet * rootPages; diff --git a/Source/RATilePager.m b/Source/RATilePager.m index d36d525..ba8b6da 100644 --- a/Source/RATilePager.m +++ b/Source/RATilePager.m @@ -14,6 +14,7 @@ #import #import +//#define ENABLE_GRID_OVERLAY @interface RAPageDelta : NSObject @property (strong) NSSet * pagesToAdd; @@ -28,6 +29,9 @@ @implementation RAPageDelta @implementation RATilePager { RATileDatabase * _database; RATextureWrapper * _defaultTexture; +#ifdef ENABLE_GRID_OVERLAY + RATextureWrapper * _overlayTexture; +#endif NSOperationQueue * _buildQueue; NSOperationQueue * _loadQueue; @@ -38,7 +42,7 @@ @implementation RATilePager { NSMutableSet * _removePages; } -@synthesize database = _database, nodes, rootPages, camera; +@synthesize database = _database, loadingContext, nodes, rootPages, camera; - (id)init { @@ -200,15 +204,15 @@ - (void)preparePageForTraversal:(RAPage *)page { - (void)requestPage:(RAPage *)page { NSAssert( page != nil, @"the requested page must be valid"); + page.lastRequestTime = [NSDate timeIntervalSinceReferenceDate]; - if ( page.geometry == nil && page.buildOp == nil ) { + if ( page.geometry == nil ) { // generate the geometry - NSBlockOperation * buildOp = [NSBlockOperation blockOperationWithBlock:^{ - page.geometry = [self createGeometryForTile:page.tile]; - page.geometry.texture = _defaultTexture; - }]; - [_buildQueue addOperation: buildOp]; - page.buildOp = buildOp; + page.geometry = [self createGeometryForTile:page.tile]; + page.geometry.texture0 = _defaultTexture; +#ifdef ENABLE_GRID_OVERLAY + page.geometry.texture1 = _overlayTexture; +#endif // request the tile image #if 1 /* set this to 0 to skip page loading and display a grid instead */ @@ -216,21 +220,34 @@ - (void)requestPage:(RAPage *)page { if ( url ) { NSURLRequest * request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:5.0]; - [NSURLConnection sendAsynchronousRequest:request queue:_loadQueue completionHandler:^(NSURLResponse* response, NSData* data, NSError* error){ + [NSURLConnection sendAsynchronousRequest:request queue:_loadQueue completionHandler:^(NSURLResponse* response, NSData* data, NSError* error) { if ( error ) { NSLog(@"URL Error loading (%@): %@", url, error); - page.image = [UIImage imageNamed:@"grid256"]; + //page.image = [UIImage imageNamed:@"grid256"]; } else { - page.image = [UIImage imageWithData:data]; - page.image = [UIImage imageWithData:UIImageJPEGRepresentation(page.image, 1.0)]; - page.needsUpdate = YES; + [EAGLContext setCurrentContext: self.loadingContext]; + + // without converting the image, I get a "data preprocessing error". I have no idea why + UIImage * image = [UIImage imageWithData:data]; + image = [UIImage imageWithData:UIImageJPEGRepresentation(image, 1.0)]; + + // create texture + GLKTextureInfo * textureInfo = nil; + NSDictionary * options = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:GLKTextureLoaderOriginBottomLeft]; + textureInfo = [GLKTextureLoader textureWithCGImage:[image CGImage] options:options error:&error]; + if ( error ) { + NSLog(@"Error loading texture: %@", error); + } else { + page.geometry.texture0 = [[RATextureWrapper alloc] initWithTextureInfo:textureInfo]; + } + + glFlush(); + [EAGLContext setCurrentContext: nil]; } }]; } #endif } - - page.lastRequestTime = [NSDate timeIntervalSinceReferenceDate]; } - (float)calculatePageTilt:(RAPage *)page { @@ -245,13 +262,10 @@ - (float)calculatePageScreenSpaceError:(RAPage *)page { // !!! this does not work so well on large, curved pages // in this case, should test all four corners of the tile and take min distance GLKVector3 center = GLKMatrix4MultiplyAndProjectVector3( self.camera.modelViewMatrix, page.bound.center ); - //double distance = GLKVector3Length(center); - double distance = -center.z; + double distance = GLKVector3Length(center); + //double distance = -center.z; // seem like this should be more accurate, but the math clearing isn't quite right, as it favors pages near the equator - /*GLKVector3 eye = GLKMatrix4MultiplyAndProjectVector3( self.camera.modelViewMatrix, GLKVector3Make(0, 0, 0) ); - double distance = GLKVector3Length(eye) - kRadiusEquator;*/ - - // !!! this should be based upon the Camera + // !!! this should be based upon the Camera parameters double theta = GLKMathDegreesToRadians(65.0f); double w = 2. * distance * tan(theta/2.); @@ -271,62 +285,29 @@ - (BOOL)isPageOnscreen:(RAPage *)page { return YES; } -- (BOOL)traversePage:(RAPage *)page collectActivePages:(NSMutableSet *)activeSet { +- (void)traversePage:(RAPage *)page collectActivePages:(NSMutableSet *)activeSet { NSAssert( page != nil, @"the traversed page must be valid"); - [self preparePageForTraversal:page]; // is the page facing away from the camera? - if ( [self calculatePageTilt:page] < -0.5f ) - return YES; + if ( [self calculatePageTilt:page] < -0.5f ) return; // should we choose to display this page? float texelError = [self calculatePageScreenSpaceError:page]; - if ( texelError < 3.5f ) { + if ( texelError < 3.f ) { // don't bother traversing if we are offscreen - if ( ! [self isPageOnscreen:page] ) return YES; + if ( ! [self isPageOnscreen:page] ) return; [self requestPage: page]; - - if ( page.isReady ) { - [activeSet addObject:page]; - - // request the parent since it will likely be needed - if ( page.parent ) [self requestPage:page.parent]; - return YES; - } else { - // try to display the immediate children - BOOL allChildrenReady = page.child1.isReady && page.child2.isReady && page.child3.isReady && page.child4.isReady; - if ( allChildrenReady ) { - [activeSet addObject:page.child1]; - [activeSet addObject:page.child2]; - [activeSet addObject:page.child3]; - [activeSet addObject:page.child4]; - return YES; - } - } + [activeSet addObject:page]; } else { - // render children - NSMutableSet * activeSubset = [NSMutableSet new]; - - // traverse children, but stop upon a page fault - if ( [self traversePage: page.child1 collectActivePages:activeSubset] && - [self traversePage: page.child2 collectActivePages:activeSubset] && - [self traversePage: page.child3 collectActivePages:activeSubset] && - [self traversePage: page.child4 collectActivePages:activeSubset] ) - { - [activeSet unionSet: activeSubset]; - return YES; - } else { - // display this node as an alternate - if ( page.isReady ) { - [activeSet addObject:page]; - return YES; - } - } + // traverse children + [self preparePageForTraversal:page]; + + [self traversePage: page.child1 collectActivePages:activeSet]; + [self traversePage: page.child2 collectActivePages:activeSet]; + [self traversePage: page.child3 collectActivePages:activeSet]; + [self traversePage: page.child4 collectActivePages:activeSet]; } - - // return value of NO indicates there was a page fault - return NO; } - (void)updateSceneGraph { @@ -335,7 +316,7 @@ - (void)updateSceneGraph { // load default texture if ( _defaultTexture == nil ) { - UIImage * image = [UIImage imageNamed:@"clear256"]; + UIImage * image = [UIImage imageNamed:@"grid256"]; NSError * err = nil; NSDictionary * options = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:GLKTextureLoaderOriginBottomLeft]; @@ -345,6 +326,19 @@ - (void)updateSceneGraph { _defaultTexture = [[RATextureWrapper alloc] initWithTextureInfo:textureInfo]; } +#ifdef ENABLE_GRID_OVERLAY + if ( _overlayTexture == nil ) { + UIImage * image = [UIImage imageNamed:@"clear256"]; + + NSError * err = nil; + NSDictionary * options = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:GLKTextureLoaderOriginBottomLeft]; + GLKTextureInfo * textureInfo = [GLKTextureLoader textureWithCGImage:[image CGImage] options:options error:&err]; + if ( err ) NSLog(@"Error loading texture: %@", err); + + _overlayTexture = [[RATextureWrapper alloc] initWithTextureInfo:textureInfo]; + } +#endif + BOOL doTraversal = ( _loadQueue.operationCount == 0 ); // begin a traversal if necessary @@ -380,18 +374,12 @@ - (void)updateSceneGraph { // add new pages [_insertPages enumerateObjectsUsingBlock:^(RAPage * page, BOOL *stop) { [nodes addChild: page.geometry]; - [page setupGL]; - }]; - - // setup all pages - [_activePages enumerateObjectsUsingBlock:^(RAPage * page, BOOL *stop) { - [page setupGL]; }]; // remove pages from scene graph [_removePages enumerateObjectsUsingBlock:^(RAPage *page, BOOL *stop) { [nodes removeChild: page.geometry]; - [page releaseGL]; + [page.geometry releaseGL]; // delete the page data if not a root page if ( ! [rootPages containsObject:page] ) {