Permalink
Browse files

Improve view frustum culling and throw animations

  • Loading branch information...
Ross Anderson Ross Anderson
Ross Anderson authored and Ross Anderson committed Jun 3, 2012
1 parent e6c8405 commit 09a590d770d725a6ef5daba3bf613ed6dacaa121
View
@@ -43,3 +43,5 @@ Bugs, Limitations, and Todos
- There are holes in the globe at the poles because there is no map tile content there.
- The tilt control should bounce when you hit the hard stops.
- The star box should fade into a blue sky box as the user zooms the camera in.
+
+![](https://github.com/RossAnderson/EarthView/raw/master/screenshot-ipad.png)
View
@@ -13,5 +13,15 @@ varying lowp vec4 fragmentColor;
void main()
{
- gl_FragColor = texture2D(texture0, fragmentTextureCoordinates) * fragmentColor;
+ lowp vec4 color = fragmentColor;
+
+ lowp vec4 texel;
+
+ texel = texture2D(texture0, fragmentTextureCoordinates);
+ color *= texel;
+
+ // add border
+ //if ( fragmentTextureCoordinates.x < 0.01 || fragmentTextureCoordinates.y < 0.01 ) color = vec4(0,0,0,1);
+
+ gl_FragColor = color;
}
@@ -28,5 +28,6 @@
- (BOOL)intersectsBoundingSphere:(RABoundingSphere *)bound;
- (RABoundingSphere *)transform:(GLKMatrix4)m;
+- (RABoundingSphere *)transformPlanar:(GLKMatrix4)m;
@end
View
@@ -8,6 +8,7 @@
#import "RABoundingSphere.h"
+#import <GLKit/GLKVector2.h>
#import <GLKit/GLKVector3.h>
#import <GLKit/GLKMatrix4.h>
#import <GLKit/GLKMathUtils.h>
@@ -125,25 +126,25 @@ - (BOOL)intersectsBoundingSphere:(RABoundingSphere *)bound
return ( GLKVector3DotProduct(diff, diff) <= (radius + bound.radius)*(radius + bound.radius));
}
-- (RABoundingSphere *)transform:(GLKMatrix4)tr
+- (RABoundingSphere *)transform:(GLKMatrix4)m
{
RABoundingSphere * newbounds = [RABoundingSphere new];
// this algorithm was ported from OpenSceneGraph: Transform.cpp
GLKVector3 x_prime = self.center;
x_prime.x += self.radius;
- x_prime = GLKMatrix4MultiplyAndProjectVector3( tr, x_prime );
+ x_prime = GLKMatrix4MultiplyAndProjectVector3( m, x_prime );
GLKVector3 y_prime = self.center;
y_prime.y += self.radius;
- y_prime = GLKMatrix4MultiplyAndProjectVector3( tr, y_prime );
+ y_prime = GLKMatrix4MultiplyAndProjectVector3( m, y_prime );
GLKVector3 z_prime = self.center;
z_prime.z += self.radius;
- z_prime = GLKMatrix4MultiplyAndProjectVector3( tr, z_prime );
+ z_prime = GLKMatrix4MultiplyAndProjectVector3( m, z_prime );
- newbounds.center = GLKMatrix4MultiplyAndProjectVector3( tr, self.center );
+ newbounds.center = GLKMatrix4MultiplyAndProjectVector3( m, self.center );
// calculate the radius from center
float x_prime_radius = GLKVector3Length( GLKVector3Subtract( x_prime, newbounds.center ) );
@@ -156,7 +157,41 @@ - (RABoundingSphere *)transform:(GLKMatrix4)tr
if (newbounds.radius < z_prime_radius) newbounds.radius = z_prime_radius;
return newbounds;
+}
+
+- (RABoundingSphere *)transformPlanar:(GLKMatrix4)m
+{
+ // this method is similar to the above but the resulting sphere is projected
+ // only in X and Y. this is useful when projecting into normalize screen space
+ // where the Z axis is logarithmic, for example
+
+ RABoundingSphere * newbounds = [RABoundingSphere new];
+
+ GLKVector3 x_prime = self.center;
+ x_prime.x += self.radius;
+ x_prime = GLKMatrix4MultiplyAndProjectVector3( m, x_prime );
+
+ GLKVector3 y_prime = self.center;
+ y_prime.y += self.radius;
+ y_prime = GLKMatrix4MultiplyAndProjectVector3( m, y_prime );
+ GLKVector3 z_prime = self.center;
+ z_prime.z += self.radius;
+ z_prime = GLKMatrix4MultiplyAndProjectVector3( m, z_prime );
+
+ newbounds.center = GLKMatrix4MultiplyAndProjectVector3( m, self.center );
+
+ // calculate the radius from center
+ float x_prime_radius = GLKVector2Length( GLKVector2Make( x_prime.x - newbounds.center.x, x_prime.y - newbounds.center.y ) );
+ float y_prime_radius = GLKVector2Length( GLKVector2Make( y_prime.x - newbounds.center.x, y_prime.y - newbounds.center.y ) );
+ float z_prime_radius = GLKVector2Length( GLKVector2Make( z_prime.x - newbounds.center.x, z_prime.y - newbounds.center.y ) );
+
+ // choose the longest radius
+ newbounds.radius = x_prime_radius;
+ if (newbounds.radius < y_prime_radius) newbounds.radius = y_prime_radius;
+ if (newbounds.radius < z_prime_radius) newbounds.radius = z_prime_radius;
+
+ return newbounds;
}
View
@@ -21,7 +21,16 @@ extern NSString * RACameraStateChangedNotification;
@property (assign) GLKMatrix4 modelViewMatrix;
@property (readonly) GLKMatrix4 projectionMatrix;
+@property (readonly) float near, far;
@property (readonly) float tanThetaOverTwo;
+@property (readonly) float cosThetaOverTwo;
+@property (readonly) float sinThetaOverTwo;
+@property (readonly) float aspect;
+
+@property (readonly) GLKVector3 leftPlaneNormal;
+@property (readonly) GLKVector3 rightPlaneNormal;
+@property (readonly) GLKVector3 topPlaneNormal;
+@property (readonly) GLKVector3 bottomPlaneNormal;
- (void)calculateProjectionForBounds:(RABoundingSphere *)bound;
View
@@ -11,12 +11,24 @@
NSString * RACameraStateChangedNotification = @"RACameraStateChangedNotification";
-@implementation RACamera
+@implementation RACamera {
+ RABoundingSphere * _bound;
+ __weak RACamera * _follow;
+
+ GLKMatrix4 _modelViewMatrix;
+ GLKMatrix4 _projectionMatrix;
+}
-@synthesize modelViewMatrix=_modelViewMatrix;
-@synthesize projectionMatrix=_projectionMatrix;
-@synthesize tanThetaOverTwo=_tanThetaOverTwo;
@synthesize viewport, fieldOfView;
+@synthesize near=_near, far=_far;
+@synthesize tanThetaOverTwo=_tanThetaOverTwo;
+@synthesize sinThetaOverTwo=_sinThetaOverTwo;
+@synthesize cosThetaOverTwo=_cosThetaOverTwo;
+@synthesize aspect=_aspect;
+@synthesize leftPlaneNormal=_leftPlaneNormal;
+@synthesize rightPlaneNormal=_rightPlaneNormal;
+@synthesize topPlaneNormal=_topPlaneNormal;
+@synthesize bottomPlaneNormal=_bottomPlaneNormal;
- (id)init
{
@@ -28,6 +40,16 @@ - (id)init
return self;
}
+- (id)copyWithZone:(NSZone *)zone {
+ RACamera * camera = [[[self class] allocWithZone:zone] init];
+ camera.viewport = self.viewport;
+ camera.fieldOfView = self.fieldOfView;
+ camera.modelViewMatrix = self.modelViewMatrix;
+ if ( _bound ) [camera calculateProjectionForBounds:_bound];
+ if ( _follow ) [camera followCamera:_follow];
+ return camera;
+}
+
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
@@ -45,20 +67,37 @@ - (void)setModelViewMatrix:(GLKMatrix4)modelViewMatrix {
[self stateUpdated];
}
+- (GLKMatrix4)projectionMatrix {
+ return _projectionMatrix;
+}
+
- (void)calculateProjectionForBounds:(RABoundingSphere *)bound {
- float aspect = fabsf(viewport.size.width / viewport.size.height);
+ _bound = bound;
+
+ _aspect = fabsf(viewport.size.width / viewport.size.height);
// calculate min/max scene distance
GLKVector3 center = GLKMatrix4MultiplyAndProjectVector3(_modelViewMatrix, bound.center);
- float minDistance = -center.z - bound.radius;
- float maxDistance = -center.z + bound.radius;
- if ( minDistance < 0.0001f ) minDistance = 0.0001f;
+ _near = -center.z - bound.radius;
+ _far = -center.z + bound.radius;
+ if ( _near < 0.0001f ) _near = 0.0001f;
- _projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(self.fieldOfView), aspect, minDistance, maxDistance);
- _tanThetaOverTwo = tan(GLKMathDegreesToRadians(self.fieldOfView)/2.);
+ _projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(self.fieldOfView), _aspect, _near, _far);
+
+ float rad = GLKMathDegreesToRadians(self.fieldOfView / 2.0f);
+ _tanThetaOverTwo = tanf(rad);
+ _sinThetaOverTwo = sinf(rad);
+ _cosThetaOverTwo = cosf(rad);
+
+ _leftPlaneNormal = GLKVector3Make( -_cosThetaOverTwo, 0, _sinThetaOverTwo );
+ _rightPlaneNormal = GLKVector3Make( _cosThetaOverTwo, 0, _sinThetaOverTwo );
+ _topPlaneNormal = GLKVector3Make( 0, _cosThetaOverTwo/_aspect, _sinThetaOverTwo );
+ _bottomPlaneNormal = GLKVector3Make( 0, -_cosThetaOverTwo/_aspect, _sinThetaOverTwo );
}
- (void)followCamera:(RACamera *)primary {
+ _follow = primary;
+
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(followCameraFromNotification:) name:RACameraStateChangedNotification object:primary];
}
View
@@ -162,8 +162,8 @@ - (id)init
_vertexDataDirty = _indexDataDirty = YES;
OSAtomicIncrement64( &sGeometryObjectCount );
- if ( sGeometryObjectCount > 500 ) {
- NSLog(@"Warning: there are now %lld geometry objects!", sGeometryObjectCount);
+ if ( sGeometryObjectCount > 600 ) {
+ NSLog(@"Warning: high geometry count = %lld", sGeometryObjectCount);
}
}
return self;
View
@@ -28,8 +28,6 @@
- (void)addGesturesToView:(UIView *)view;
-//- (GLKMatrix4)modelViewMatrix;
-
- (void)flyToRegion:(CLRegion *)region;
@end
View
@@ -16,7 +16,7 @@
static const RAPolarCoordinate kDefaultVelocity = { 0, -10, 0 };
static const CGFloat kAnimationDuration = 1.0f;
-static const CGFloat kMinimumAnimatedAngle = 2.0f;
+static const CGFloat kMinimumAnimatedVelocity = 15.0f;
typedef struct {
@@ -357,65 +357,71 @@ - (void)move:(id)sender {
{
CGPoint vel = [pan velocityInView:view];
+ // don't animate small motions
+ if ( fabs( MAX(vel.x, vel.y) ) < kMinimumAnimatedVelocity ) {
+ sAction = GestureNone;
+ break;
+ }
+
switch (sAction) {
case GestureRotate:
{
// calculate how much movement
- CGFloat angle = vel.x * 0.03;
- if ( fabs(angle) < kMinimumAnimatedAngle ) break;
+ double angle = vel.x * 0.03;
TPPropertyAnimation *anim = [TPPropertyAnimation propertyAnimationWithKeyPath:@"azimuth"];
anim.duration = kAnimationDuration;
anim.fromValue = [NSNumber numberWithDouble:_state.azimuth];
anim.toValue = [NSNumber numberWithDouble:_state.azimuth + angle];
anim.timing = TPPropertyAnimationTimingEaseOut;
[anim beginWithTarget:self];
+
break;
}
case GestureTilt:
{
// calculate how much movement
- CGFloat angle = vel.y * 0.03;
- if ( fabs(angle) < kMinimumAnimatedAngle ) break;
+ double angle = vel.y * 0.03;
TPPropertyAnimation *anim = [TPPropertyAnimation propertyAnimationWithKeyPath:@"elevation"];
anim.duration = kAnimationDuration;
anim.fromValue = [NSNumber numberWithDouble:_state.elevation];
anim.toValue = [NSNumber numberWithDouble:_state.elevation + angle];
anim.timing = TPPropertyAnimationTimingEaseOut;
[anim beginWithTarget:self];
+
break;
}
case GestureGeoDrag:
{
// continue movement in the same direction
- CGPoint dir = CGPointMake( _state.longitude - startState.longitude, _state.latitude - startState.latitude );
- dir.x = NormalizeLongitude(dir.x);
+ double dir_lon = NormalizeLongitude( _state.longitude - startState.longitude );
+ double dir_lat = _state.latitude - startState.latitude;
- CGFloat length = sqrt( dir.x*dir.x + dir.y*dir.y );
- if ( length < 1 ) break;
- dir.x /= length;
- dir.y /= length;
+ double length = sqrt( dir_lon*dir_lon + dir_lat*dir_lat );
+ if ( length == 0.0f ) break;
+ dir_lon /= length;
+ dir_lat /= length;
// calculate how much movement
- CGFloat speed = sqrt( vel.x*vel.x + vel.y*vel.y );
- CGFloat angle = ( _state.distance / 1e7 ) * speed * 0.03;
- if ( fabs(angle) < kMinimumAnimatedAngle ) break;
+ double speed = sqrt( vel.x*vel.x + vel.y*vel.y );
+ double angle = ( _state.distance / 1e7 ) * speed * 0.03;
- CGPoint destination = CGPointMake( _state.longitude + dir.x*angle, _state.latitude + dir.y*angle );
+ double dest_lon = _state.longitude + dir_lon*angle;
+ double dest_lat = _state.latitude + dir_lat*angle;
// zoom to that location
TPPropertyAnimation *anim = [TPPropertyAnimation propertyAnimationWithKeyPath:@"latitude"];
anim.duration = kAnimationDuration;
anim.fromValue = [NSNumber numberWithDouble:_state.latitude];
- anim.toValue = [NSNumber numberWithDouble:destination.y];
+ anim.toValue = [NSNumber numberWithDouble:dest_lat];
anim.timing = TPPropertyAnimationTimingEaseOut;
[anim beginWithTarget:self];
anim = [TPPropertyAnimation propertyAnimationWithKeyPath:@"longitude"];
anim.duration = kAnimationDuration;
anim.fromValue = [NSNumber numberWithDouble:_state.longitude];
- anim.toValue = [NSNumber numberWithDouble:destination.x];
+ anim.toValue = [NSNumber numberWithDouble:dest_lon];
anim.timing = TPPropertyAnimationTimingEaseOut;
[anim beginWithTarget:self];
@@ -424,11 +430,10 @@ - (void)move:(id)sender {
case GestureAxisSpin:
{
// calculate how much movement
- CGFloat speed = vel.x;
- CGFloat angle = -( _state.distance / 1e7 ) * speed * 0.1;
- if ( fabs(angle) < kMinimumAnimatedAngle ) break;
+ double speed = vel.x;
+ double angle = -( _state.distance / 1e7 ) * speed * 0.1;
- float destination = _state.longitude + angle;
+ double destination = _state.longitude + angle;
// spin the globe
TPPropertyAnimation *anim = [TPPropertyAnimation propertyAnimationWithKeyPath:@"longitude"];
View
@@ -63,12 +63,6 @@ - (float)calculateScreenSpaceErrorWithCamera:(RACamera *)camera {
GLKVector3 center = GLKMatrix4MultiplyAndProjectVector3( camera.modelViewMatrix, self.bound.center );
double distance = GLKVector3Length(center);
- /*
- const GLKMatrix4 m = camera.modelViewMatrix;
- GLKVector3 cameraPos = GLKVector3Make( m.m[12], m.m[13], m.m[14] );
- float distance = GLKVector3Distance(cameraPos, self.bound.center);
- */
-
// convert object error to screen error
CGSize size = camera.viewport.size;
float epsilon = ( 2. * self.bound.radius ) / 256.; // object error
@@ -78,11 +72,20 @@ - (float)calculateScreenSpaceErrorWithCamera:(RACamera *)camera {
}
- (BOOL)isOnscreenWithCamera:(RACamera *)camera {
- GLKMatrix4 modelViewProjectionMatrix = GLKMatrix4Multiply( camera.projectionMatrix, camera.modelViewMatrix );
+ // convert the bounding sphere center into camera space
+ GLKVector3 s = GLKMatrix4MultiplyAndProjectVector3( camera.modelViewMatrix, self.bound.center );
- RABoundingSphere * sb = [self.bound transform:modelViewProjectionMatrix];
- if ( sb.center.x + sb.radius < -1 || sb.center.x - sb.radius > 1 ) return NO;
- if ( sb.center.y + sb.radius < -1 || sb.center.y - sb.radius > 1 ) return NO;
+ float radius = self.bound.radius * 1.5f;
+
+ // test against near/far planes
+ if ( s.z - radius > -camera.near ) return NO;
+ if ( s.z + radius < -camera.far ) return NO;
+
+ // left, right, top, bottom planes
+ if ( GLKVector3DotProduct( camera.leftPlaneNormal, s ) > radius ) return NO;
+ if ( GLKVector3DotProduct( camera.rightPlaneNormal, s ) > radius ) return NO;
+ if ( GLKVector3DotProduct( camera.topPlaneNormal, s ) > radius ) return NO;
+ if ( GLKVector3DotProduct( camera.bottomPlaneNormal, s ) > radius ) return NO;
return YES;
}
View
@@ -14,6 +14,8 @@
@interface RARenderVisitor : RANodeVisitor
+@property (readonly) NSString * statsString;
+
@property (strong) RACamera * camera;
@property (assign) GLKVector3 lightPosition;
@property (assign) GLKVector4 lightAmbientColor;
Oops, something went wrong.

0 comments on commit 09a590d

Please sign in to comment.