Created
March 10, 2017 08:11
-
-
Save lachlanhurst/0d0f9f79868cbd0e7053cde62ab22822 to your computer and use it in GitHub Desktop.
Pulse effect in SceneKit - Objective C
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// GameViewController.h | |
// pulseeffectobjectivec | |
// | |
// Created by Lachlan Hurst on 10/3/17. | |
// Copyright © 2017 Lachlan Hurst. All rights reserved. | |
// | |
#import <UIKit/UIKit.h> | |
#import <SceneKit/SceneKit.h> | |
@interface GameViewController : UIViewController | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// GameViewController.m | |
// pulseeffectobjectivec | |
// | |
// Created by Lachlan Hurst on 10/3/17. | |
// Copyright © 2017 Lachlan Hurst. All rights reserved. | |
// | |
#import "GameViewController.h" | |
@implementation GameViewController | |
- (void)viewDidLoad | |
{ | |
[super viewDidLoad]; | |
// create a new scene | |
SCNScene *scene = [SCNScene scene]; | |
// create and add a camera to the scene | |
SCNNode *cameraNode = [SCNNode node]; | |
cameraNode.camera = [SCNCamera camera]; | |
[scene.rootNode addChildNode:cameraNode]; | |
// place the camera | |
cameraNode.position = SCNVector3Make(0, 0, 15); | |
// create and add a light to the scene | |
SCNNode *lightNode = [SCNNode node]; | |
lightNode.light = [SCNLight light]; | |
lightNode.light.type = SCNLightTypeOmni; | |
lightNode.position = SCNVector3Make(0, 10, 10); | |
[scene.rootNode addChildNode:lightNode]; | |
// create and add an ambient light to the scene | |
SCNNode *ambientLightNode = [SCNNode node]; | |
ambientLightNode.light = [SCNLight light]; | |
ambientLightNode.light.type = SCNLightTypeAmbient; | |
ambientLightNode.light.color = [UIColor darkGrayColor]; | |
[scene.rootNode addChildNode:ambientLightNode]; | |
SCNNode *boxNode = [SCNNode nodeWithGeometry:[SCNBox boxWithWidth:1.0 height:1.0 length:1.0 chamferRadius:0.0]]; | |
boxNode.geometry.firstMaterial.diffuse.contents = [UIColor redColor]; | |
[scene.rootNode addChildNode:boxNode]; | |
CGFloat pulseSize = 5.0; | |
SCNPlane *pulsePlane = [SCNPlane planeWithWidth:pulseSize height:pulseSize]; | |
[pulsePlane.firstMaterial setDoubleSided:true]; | |
pulsePlane.firstMaterial.diffuse.contents = [UIColor blueColor]; | |
SCNNode *pulseNode = [SCNNode nodeWithGeometry:pulsePlane]; | |
NSString *pulseShaderModifier = [NSString stringWithFormat: | |
@"#pragma transparent; \n" | |
"vec4 originalColour = _surface.diffuse; \n" | |
"vec4 transformed_position = u_inverseModelTransform * u_inverseViewTransform * vec4(_surface.position, 1.0); \n" | |
"vec2 xy = vec2(transformed_position.x, transformed_position.y); \n" | |
"float xyLength = length(xy); \n" | |
"float xyLengthNormalised = xyLength/%f; \n" | |
"float speedFactor = 1.5; \n" | |
"float maxDist = fmod(u_time, speedFactor) / speedFactor; \n" | |
"float distbasedalpha = step(maxDist, xyLengthNormalised); \n" | |
"distbasedalpha = max(distbasedalpha, maxDist); \n" | |
"_surface.diffuse = mix(originalColour, vec4(0.0), distbasedalpha);", pulseSize/2.0]; | |
NSDictionary *smdict = [NSDictionary dictionaryWithObject:pulseShaderModifier forKey:SCNShaderModifierEntryPointSurface]; | |
[pulsePlane.firstMaterial setShaderModifiers:smdict]; | |
[boxNode addChildNode:pulseNode]; | |
SCNLookAtConstraint *pulseNodeConstraint = [SCNLookAtConstraint lookAtConstraintWithTarget:cameraNode]; | |
NSArray *constraints = [NSArray arrayWithObject:pulseNodeConstraint]; | |
[pulseNode setConstraints:constraints]; | |
// animate the 3d object | |
[boxNode runAction:[SCNAction repeatActionForever:[SCNAction rotateByX:0 y:2 z:0 duration:1]]]; | |
// retrieve the SCNView | |
SCNView *scnView = (SCNView *)self.view; | |
// set the scene to the view | |
scnView.scene = scene; | |
// allows the user to manipulate the camera | |
scnView.allowsCameraControl = YES; | |
// show statistics such as fps and timing information | |
scnView.showsStatistics = YES; | |
// configure the view | |
scnView.backgroundColor = [UIColor lightGrayColor]; | |
// add a tap gesture recognizer | |
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)]; | |
NSMutableArray *gestureRecognizers = [NSMutableArray array]; | |
[gestureRecognizers addObject:tapGesture]; | |
[gestureRecognizers addObjectsFromArray:scnView.gestureRecognizers]; | |
scnView.gestureRecognizers = gestureRecognizers; | |
} | |
- (void) handleTap:(UIGestureRecognizer*)gestureRecognize | |
{ | |
// retrieve the SCNView | |
SCNView *scnView = (SCNView *)self.view; | |
// check what nodes are tapped | |
CGPoint p = [gestureRecognize locationInView:scnView]; | |
NSArray *hitResults = [scnView hitTest:p options:nil]; | |
// check that we clicked on at least one object | |
if([hitResults count] > 0){ | |
// retrieved the first clicked object | |
SCNHitTestResult *result = [hitResults objectAtIndex:0]; | |
// get its material | |
SCNMaterial *material = result.node.geometry.firstMaterial; | |
// highlight it | |
[SCNTransaction begin]; | |
[SCNTransaction setAnimationDuration:0.5]; | |
// on completion - unhighlight | |
[SCNTransaction setCompletionBlock:^{ | |
[SCNTransaction begin]; | |
[SCNTransaction setAnimationDuration:0.5]; | |
material.emission.contents = [UIColor blackColor]; | |
[SCNTransaction commit]; | |
}]; | |
material.emission.contents = [UIColor redColor]; | |
[SCNTransaction commit]; | |
} | |
} | |
- (BOOL)shouldAutorotate | |
{ | |
return YES; | |
} | |
- (BOOL)prefersStatusBarHidden { | |
return YES; | |
} | |
- (UIInterfaceOrientationMask)supportedInterfaceOrientations | |
{ | |
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { | |
return UIInterfaceOrientationMaskAllButUpsideDown; | |
} else { | |
return UIInterfaceOrientationMaskAll; | |
} | |
} | |
- (void)didReceiveMemoryWarning | |
{ | |
[super didReceiveMemoryWarning]; | |
// Release any cached data, images, etc that aren't in use. | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment