Scene Kit Session WWDC 2013/Sources/Slides/ASCSlideNodeDelegate_Alternate.m
#import "ASCPresentation.h" |
#import "ASCTextManager.h" |
#import "ASCSlide.h" |
#import "Utils.h" |
#import "GLUtils.h" |
#define FlareWidth 3.8 |
#define FlareHeight 3.8 |
#define kFlareVerticesCount 16 |
#define kFlareIndicesCount 18*3 |
enum { |
QUAD_ATTRIB_POS, |
QUAD_ATTRIB_UV |
}; |
GLuint _flareVAO; |
GLuint _flareVBO; |
GLuint _flareIBO; |
GLuint _flareProgram; |
GLuint _mvpLoc; |
GLuint _timeLoc; |
GLuint _factorLoc; |
GLuint _resolutionLoc; |
CFAbsoluteTime _startTime; |
CGFloat _factor; |
CGFloat _targetFactor; |
@interface MyRendererDelegate : NSObject <SCNNodeRendererDelegate> |
@property BOOL showWireframe; |
@end |
static MyRendererDelegate *rendererDelegate; |
typedef struct VertexUV |
{ |
GLfloat position[3]; |
GLfloat uv0[2]; // we insert the vertex index in the z coord |
} VertexUV; |
@implementation ASCSlideNodeDelegate |
{ |
} |
- (void)createFlareGeometry:(ASCPresentation *)controller |
{ |
NSOpenGLContext *ctx = [controller.view openGLContext]; |
[ctx makeCurrentContext]; |
glGenVertexArraysAPPLE(1, &_flareVAO); |
glBindVertexArrayAPPLE(_flareVAO); |
glGenBuffers(1, &_flareVBO); |
glBindBuffer(GL_ARRAY_BUFFER, _flareVBO); |
VertexUV vertices[kFlareVerticesCount] = { |
{{-1.f, 1.f, 0.f}, {0.f, 1.f}}, // TL |
{{ 1.f, 1.f, 0.f}, {1.f, 1.f}}, // TR |
{{ 1.f,-1.f, 0.f}, {1.f, 0.f}}, // BR |
{{-1.f,-1.f, 0.f}, {0.f, 0.f}} // BL |
}; |
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW); |
glVertexAttribPointer(QUAD_ATTRIB_POS, 3, GL_FLOAT, GL_FALSE, sizeof(VertexUV), (void*)offsetof(VertexUV,position)); |
glEnableVertexAttribArray(QUAD_ATTRIB_POS); |
glVertexAttribPointer(QUAD_ATTRIB_UV, 2, GL_FLOAT, GL_TRUE, sizeof(VertexUV), (void*)offsetof(VertexUV,uv0)); |
glEnableVertexAttribArray(QUAD_ATTRIB_UV); |
// create an IBO and capture it in the VAO |
glGenBuffers(1, &_flareIBO); |
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _flareIBO); |
GLchar indices[kFlareIndicesCount] = { |
0,4,5, 0,5,6, 0,6,7, 0,7,1, 1,7,8, 1,8,9, |
15,4,0, 15,0,3, 3,0,1, 3,1,2, 2,1,9, 2,9,10, |
14,15,3, 14,3,13, 13,3,2, 13,2,12, 12,2,11, 11,2,10 |
}; |
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); |
glBindVertexArrayAPPLE(0); |
AttribLocation attrib[] = { |
{QUAD_ATTRIB_POS, "a_pos"}, |
{QUAD_ATTRIB_UV, "a_uv"}, |
{0, 0} |
}; |
_flareProgram = CreateProgramWithAttributesLocation(@"NodeDelegate", attrib); |
_mvpLoc = glGetUniformLocation ( _flareProgram, "u_mvp" ); |
} |
- (NSUInteger) numberOfSteps |
{ |
return 3; |
} |
- (SCNNode *) createCubeWithFlareWithWidth:(CGFloat)width height:(CGFloat)height length:(CGFloat)length outerMaterial:(SCNMaterial*)outerMaterial |
{ |
SCNNode *object = [SCNNode node]; |
object.geometry = [SCNBox boxWithWidth:width height:height length:length chamferRadius:0.05]; |
SCNMaterial *whiteMaterial = [SCNMaterial material]; |
whiteMaterial.emission.contents = [NSColor whiteColor]; |
object.geometry.materials = @[whiteMaterial, outerMaterial, outerMaterial, outerMaterial, outerMaterial, outerMaterial]; |
[self.ground addChildNode:object]; |
if(rendererDelegate == nil){ |
rendererDelegate = [[MyRendererDelegate alloc] init]; |
} |
SCNNode *flare = [SCNNode node]; |
flare.position = SCNVector3Make(0, 0, length * 0.5 + 0.01); |
flare.geometry = [SCNPlane planeWithWidth:width height:height]; |
flare.rendererDelegate = rendererDelegate; |
flare.renderingOrder = 1; |
[object addChildNode:flare]; |
return object; |
} |
- (void) setup:(ASCPresentation *)controller |
{ |
ASCTextManager *text = [self setupTextManager:NO]; |
[text addText:@"Extending Scene Kit with OpenGL" withType:ASCTextTypeTitle level:0]; |
[text addText:@"Node delegate rendering" withType:ASCTextTypeSubTitle level:0]; |
[text addText:@"Custom OpenGL code per node" withType:ASCTextTypeBullet level:0]; |
[text addText:@"Overrides Scene Kit’s rendering" withType:ASCTextTypeBullet level:0]; |
[text addText:@"Transform and geometry information are provided by Scene Kit" withType:ASCTextTypeBullet level:0]; |
SCNMaterial *redMaterial = [SCNMaterial material]; |
redMaterial.diffuse.contents = [NSColor redColor]; |
redMaterial.specular.contents = [NSColor whiteColor]; |
redMaterial.locksAmbientWithDiffuse = YES; |
SCNNode *object = [self createCubeWithFlareWithWidth:4 height:4 length:4 outerMaterial:redMaterial]; |
object.position = SCNVector3Make(0, 2, 8); |
[self.ground addChildNode:object]; |
[self createFlareGeometry:controller]; |
//animate |
{ |
CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"rotation"]; |
rotationAnimation.duration = 10.0; |
rotationAnimation.repeatCount = MAXFLOAT; |
rotationAnimation.toValue = [NSValue valueWithSCNVector4:SCNVector4Make(0, 1, 0, M_PI*2)]; |
[object addAnimation:rotationAnimation forKey:nil]; |
} |
SCNNode *axis = [SCNNode node]; |
[object addChildNode:axis]; |
axis.position = SCNVector3Make(0, 2, 0); |
axis.name = @"axis"; |
{ |
CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"rotation"]; |
rotationAnimation.duration = 3.0; |
rotationAnimation.repeatCount = MAXFLOAT; |
rotationAnimation.toValue = [NSValue valueWithSCNVector4:SCNVector4Make(0, 1, 0, -M_PI*2)]; |
[axis addAnimation:rotationAnimation forKey:nil]; |
} |
} |
- (void) presentStepIndex:(NSUInteger)index withController:(ASCPresentation *)controller |
{ |
switch (index) { |
case 0: |
break; |
case 1: |
rendererDelegate.showWireframe = YES; |
break; |
case 2: |
rendererDelegate.showWireframe = NO; |
[self.textManager flipOutTextType:ASCTextTypeBullet]; |
SCNMaterial *redMaterial = [SCNMaterial material]; |
redMaterial.diffuse.contents = [NSColor colorWithCalibratedRed:0.4 green:0.01 blue:0.01 alpha:1.0]; |
redMaterial.specular.contents = [NSColor whiteColor]; |
redMaterial.locksAmbientWithDiffuse = YES; |
SCNNode *axis = [self.ground childNodeWithName:@"axis" recursively:YES]; |
for (int i = 0; i < 4; ++i) { |
SCNNode *cube = [self createCubeWithFlareWithWidth:0.5 height:FlareHeight length:0.5 outerMaterial:redMaterial]; |
[cube setPosition:SCNVector3Make(sin(M_PI_2*i), FlareHeight/2, cos(M_PI_2*i))]; |
[cube setRotation:SCNVector4Make(0, 1, 0, M_PI_2 * i)]; |
[axis addChildNode:cube]; |
} |
break; |
} |
} |
//TODO: Move to Utilities |
//NO, TODO: use GLKit |
static inline SCNVector3 SCNVector3Add(SCNVector3 a, SCNVector3 b) |
{ |
return SCNVector3Make(a.x + b.x, a.y + b.y, a.z + b.z); |
} |
static inline SCNVector3 SCNVector3Sub(SCNVector3 a, SCNVector3 b) |
{ |
return SCNVector3Make(a.x - b.x, a.y - b.y, a.z - b.z); |
} |
static inline SCNVector3 SCNVector3Neg(SCNVector3 a) |
{ |
return SCNVector3Make(-a.x, -a.y, -a.z); |
} |
static inline SCNVector3 SCNVector3Mul(SCNVector3 a, CGFloat b) |
{ |
return SCNVector3Make(a.x * b, a.y * b, a.z * b); |
} |
static inline SCNVector3 SCNVector3Div(SCNVector3 a, CGFloat b) |
{ |
return SCNVector3Make(a.x / b, a.y / b, a.z / b); |
} |
static inline SCNVector3 SCNVector3Cross(SCNVector3 a, SCNVector3 b) |
{ |
return SCNVector3Make(a.y * b.z - b.y * a.z, |
a.z * b.x - b.z * a.x, |
a.x * b.y - b.x * a.y); |
} |
static inline CGFloat SCNVector3Dot(SCNVector3 a, SCNVector3 b) |
{ |
return a.x * b.x + a.y * b.y + a.z * b.z; |
} |
static inline CGFloat SCNVector3Length(SCNVector3 a) |
{ |
return sqrt(SCNVector3Dot(a, a)); |
} |
static inline SCNVector3 SCNVector3Normalize(SCNVector3 a) |
{ |
CGFloat len = SCNVector3Length(a); |
return SCNVector3Make(a.x / len, a.y / len, a.z / len); |
} |
typedef struct { |
SCNVector3 normal; |
CGFloat D; |
} SCNPlane3; |
static inline SCNPlane3 SCNPlane3MakeFromPoints(SCNVector3 p0, SCNVector3 p1, SCNVector3 p2) |
{ |
SCNPlane3 plane; |
plane.normal.x = p0.y*(p1.z-p2.z) + p1.y*(p2.z-p0.z) + p2.y*(p0.z-p1.z); |
plane.normal.y = p0.z*(p1.x-p2.x) + p1.z*(p2.x-p0.x) + p2.z*(p0.x-p1.x); |
plane.normal.z = p0.x*(p1.y-p2.y) + p1.x*(p2.y-p0.y) + p2.x*(p0.y-p1.y); |
plane.D = -( p0.x*( p1.y*p2.z - p2.y*p1.z ) + |
p1.x*(p2.y*p0.z - p0.y*p2.z) + |
p2.x*(p0.y*p1.z - p1.y*p0.z) ); |
CGFloat l = SCNVector3Length(plane.normal); |
if(l==0){ // fail |
return plane; |
} |
plane.normal = SCNVector3Div(plane.normal, l); |
plane.D /= l; |
return plane; |
} |
@end |
typedef struct |
{ |
SCNVector3 position; |
CGPoint uv0; |
} SCNVertexPosUV; |
@implementation MyRendererDelegate |
- (void)renderNode:(SCNNode *)node renderer:(SCNRenderer *)renderer arguments:(NSDictionary *)arguments |
{ |
glDisable(GL_CULL_FACE); |
glDisable(GL_DEPTH_TEST); |
glDepthMask(false); |
glEnable(GL_BLEND); |
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
SCNVector3 bmin, bmax; |
[node getBoundingBoxMin:&bmin max:&bmax]; |
CGFloat width = (bmax.x - bmin.x) * 0.5; |
CGFloat height = (bmax.y - bmin.y) * 0.5; |
SCNVector3 planePts[4]; |
planePts[0] = [[node presentationNode] convertPosition:SCNVector3Make(-width, height, 0) toNode:renderer.pointOfView]; |
planePts[1] = [[node presentationNode] convertPosition:SCNVector3Make( width, height, 0) toNode:renderer.pointOfView]; |
planePts[2] = [[node presentationNode] convertPosition:SCNVector3Make( width, -height, 0) toNode:renderer.pointOfView]; |
planePts[3] = [[node presentationNode] convertPosition:SCNVector3Make(-width, -height, 0) toNode:renderer.pointOfView]; |
SCNVector3 viewPos = SCNVector3Make(0, 0, 0); |
SCNPlane3 plane = SCNPlane3MakeFromPoints(planePts[0], planePts[2], planePts[1]); |
CGFloat distFromPlane = SCNVector3Dot(plane.normal, viewPos) + plane.D; |
if ( distFromPlane <= 0 ) { // flare is not visible |
return; |
} |
SCNVector3 dir = SCNVector3Normalize(planePts[0]); |
CGFloat frontAngle = MIN(- 8.0 * SCNVector3Dot(plane.normal, dir), 1.0); |
SCNVertexPosUV vert[kFlareVerticesCount]; |
SCNVector3 edgeDir[4][3]; |
// calculate vector directions |
for ( int i = 0 ; i < 4 ; i++ ) { |
vert[i].position = planePts[ i ]; |
vert[i].uv0.x = vert[i].uv0.y = 0.5 * frontAngle; |
SCNVector3 toEye = SCNVector3Sub(planePts[ i ], viewPos); |
toEye = SCNVector3Normalize(toEye); |
SCNVector3 d1 = SCNVector3Sub(planePts[ (i+1)%4 ], viewPos); |
d1 = SCNVector3Normalize(d1); |
edgeDir[i][1] = SCNVector3Cross(toEye, d1); |
edgeDir[i][1] = SCNVector3Neg(SCNVector3Normalize(edgeDir[i][1])); |
SCNVector3 d2 = SCNVector3Sub(planePts[ (i+3)%4 ], viewPos); |
d2 = SCNVector3Normalize(d2); |
edgeDir[i][0] = SCNVector3Cross(toEye, d2); |
edgeDir[i][0] = SCNVector3Normalize(edgeDir[i][0]); |
edgeDir[i][2] = SCNVector3Add(edgeDir[i][0], edgeDir[i][1]); |
edgeDir[i][2] = SCNVector3Normalize(edgeDir[i][2]); |
} |
CGFloat spread = (width + height) / 2; |
// static float accum = 0.0; |
// accum += 0.01; |
// spread += (spread * 0.15) * sin(7.0*cos(9.0*accum)); |
// build all the points |
vert[ 4].position = SCNVector3Add(planePts[0], SCNVector3Mul(edgeDir[0][0], spread)); |
vert[ 5].position = SCNVector3Add(planePts[0], SCNVector3Mul(edgeDir[0][2], spread)); |
vert[ 6].position = SCNVector3Add(planePts[0], SCNVector3Mul(edgeDir[0][1], spread)); |
vert[ 7].position = SCNVector3Add(planePts[1], SCNVector3Mul(edgeDir[1][0], spread)); |
vert[ 8].position = SCNVector3Add(planePts[1], SCNVector3Mul(edgeDir[1][2], spread)); |
vert[ 9].position = SCNVector3Add(planePts[1], SCNVector3Mul(edgeDir[1][1], spread)); |
vert[10].position = SCNVector3Add(planePts[2], SCNVector3Mul(edgeDir[2][0], spread)); |
vert[11].position = SCNVector3Add(planePts[2], SCNVector3Mul(edgeDir[2][2], spread)); |
vert[12].position = SCNVector3Add(planePts[2], SCNVector3Mul(edgeDir[2][1], spread)); |
vert[13].position = SCNVector3Add(planePts[3], SCNVector3Mul(edgeDir[3][0], spread)); |
vert[14].position = SCNVector3Add(planePts[3], SCNVector3Mul(edgeDir[3][2], spread)); |
vert[15].position = SCNVector3Add(planePts[3], SCNVector3Mul(edgeDir[3][1], spread)); |
for ( int i = 4 ; i < kFlareVerticesCount ; i++ ) { |
SCNVector3 dir = SCNVector3Sub(vert[i].position, viewPos); |
float len = SCNVector3Length(dir); |
dir = SCNVector3Div(dir, len); |
float ang = SCNVector3Dot(dir, plane.normal); |
float newLen = -( distFromPlane / ang ); |
if ( newLen > 0 && newLen < len ) { |
vert[i].position = SCNVector3Add(viewPos, SCNVector3Mul(dir, newLen)); |
} |
vert[i].uv0.x = 0.0; |
vert[i].uv0.y = 0.0; |
} |
glBindBuffer(GL_ARRAY_BUFFER, _flareVBO); |
VertexUV* vboVertices = (VertexUV*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); |
for (int i = 0; i < kFlareVerticesCount; ++i) { |
vboVertices[i].position[0] = vert[i].position.x; |
vboVertices[i].position[1] = vert[i].position.y; |
vboVertices[i].position[2] = vert[i].position.z; |
vboVertices[i].uv0[0] = vert[i].uv0.x; |
vboVertices[i].uv0[1] = vert[i].uv0.y; |
} |
// memcpy(vboVertices, vertices, sizeof(vertices[0]) * kFlareVerticesCount); |
glUnmapBuffer(GL_ARRAY_BUFFER); |
glBindBuffer(GL_ARRAY_BUFFER, 0); |
glBindVertexArrayAPPLE(_flareVAO); |
glUseProgram(_flareProgram); |
NSValue * mvpVal = [arguments objectForKey:SCNProjectionTransform]; |
CATransform3D mvpTrans = [mvpVal CATransform3DValue]; |
GLKMatrix4 mvp = GLKMatrix4FromCATransform3D(mvpTrans); |
glUniformMatrix4fv(_mvpLoc, 1, NO, mvp.m); |
// Debug Wireframe |
if (self.showWireframe) { |
glPolygonMode(GL_FRONT, GL_LINE); |
glPolygonMode(GL_BACK, GL_LINE); |
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); |
glDrawElements(GL_TRIANGLES, kFlareIndicesCount, GL_UNSIGNED_BYTE, 0); |
glPolygonMode(GL_FRONT, GL_FILL); |
glPolygonMode(GL_BACK, GL_FILL); |
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
} |
glDrawElements(GL_TRIANGLES, kFlareIndicesCount, GL_UNSIGNED_BYTE, 0); |
glBindVertexArrayAPPLE(0); |
glDisable(GL_BLEND); |
glEnable(GL_DEPTH_TEST); |
glDepthMask(true); |
glEnable(GL_CULL_FACE); |
} |
@end |
Copyright © 2014 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2014-01-07