Animation when activating parts of the background cube

This commit is contained in:
Charles Magahern
2015-12-30 16:56:15 -08:00
parent 9cfd75fa64
commit 00fcca6893
2 changed files with 84 additions and 8 deletions
@@ -19,11 +19,14 @@ class BackgroundViewController: UIViewController {
private var _sceneView: SCNView? private var _sceneView: SCNView?
private var _cameraNode: SCNNode = SCNNode() private var _cameraNode: SCNNode = SCNNode()
private var _cubletsNode: SCNNode = SCNNode() private var _cubletsNode: SCNNode = SCNNode()
private var _cublets: [SCNNode] = []
static private let cubletsDimensions = 5 static private let cubletsDimensions = 5
static private let cubletsSize = 1.0 static private let cubletsSize = 1.0
static private let cubletsSpacing = 2.0 static private let cubletsSpacing = 2.0
// MARK: Overrides
override func loadView() override func loadView()
{ {
let opts = [SCNPreferredRenderingAPIKey : SCNRenderingAPI.OpenGLES2.rawValue] let opts = [SCNPreferredRenderingAPIKey : SCNRenderingAPI.OpenGLES2.rawValue]
@@ -31,7 +34,7 @@ class BackgroundViewController: UIViewController {
view.backgroundColor = UIColor.blackColor() view.backgroundColor = UIColor.blackColor()
view.scene = _scene view.scene = _scene
view.allowsCameraControl = false view.allowsCameraControl = false
/* view.showsStatistics = true */ view.showsStatistics = true
_sceneView = view _sceneView = view
self.view = view self.view = view
@@ -59,6 +62,45 @@ class BackgroundViewController: UIViewController {
_sceneView?.stop(nil) _sceneView?.stop(nil)
} }
// MARK: API
func setPercentActivated(percentage: Float, animated: Bool)
{
let cubletsCount = _cublets.count
let cubletsToActivate = UInt(percentage * Float(cubletsCount))
var shuffledCublets = _cublets
var cubletsActivated: UInt = 0
for i in 0 ..< cubletsCount {
let rnd = Int(arc4random_uniform(UInt32(cubletsCount)))
let tmp = shuffledCublets[rnd]
shuffledCublets[rnd] = shuffledCublets[i]
shuffledCublets[i] = tmp
}
for cublet in shuffledCublets {
_setCubletActivated(cublet, activated: (cubletsActivated < cubletsToActivate))
++cubletsActivated
}
if (animated) {
let rx = Float(arc4random()) / Float(UInt32.max)
let ry = Float(arc4random()) / Float(UInt32.max)
let rz = Float(arc4random()) / Float(UInt32.max)
let rotAxis = SCNVector3Make(rx, ry, rz)
let rotCoeff = Float(arc4random() % 2 == 0 ? -1.0 : 1.0)
let rotAngle = CGFloat(rotCoeff * π / 4.0)
let rotAction = SCNAction.rotateByAngle(rotAngle, aroundAxis: rotAxis, duration: 0.8)
rotAction.timingMode = .Linear
rotAction.timingFunction = { (t: Float) -> Float in
return min(((log10(4.0 * (t + 0.03)) + 1.0) / 1.5), 1.0)
}
_cubletsNode.runAction(rotAction)
}
}
// MARK: Internal // MARK: Internal
internal func _setupCamera() internal func _setupCamera()
@@ -92,6 +134,8 @@ class BackgroundViewController: UIViewController {
internal func _setupModel() internal func _setupModel()
{ {
_cublets.removeAll()
let sz = Float(BackgroundViewController.cubletsSize) let sz = Float(BackgroundViewController.cubletsSize)
let dim = BackgroundViewController.cubletsDimensions let dim = BackgroundViewController.cubletsDimensions
let dimf = Float(dim) let dimf = Float(dim)
@@ -99,15 +143,13 @@ class BackgroundViewController: UIViewController {
let volume = (dimf * sz) + ((dimf - 1.0) * spacing) let volume = (dimf * sz) + ((dimf - 1.0) * spacing)
let orig = -volume / 2.0 let orig = -volume / 2.0
// setup material // setup material and geometry. each node needs its own material for activation effect.
let geom = SCNBox(width: CGFloat(sz), height: CGFloat(sz), length: CGFloat(sz), chamferRadius: 0.0)
let material = SCNMaterial() let material = SCNMaterial()
material.diffuse.contents = UIColor.whiteColor() material.diffuse.contents = UIColor.whiteColor()
material.transparency = 0.75 material.transparency = 0.75
// setup geometry // generate nodes for each cublet
let geom = SCNBox(width: CGFloat(sz), height: CGFloat(sz), length: CGFloat(sz), chamferRadius: 0.0)
geom.firstMaterial = material
for var xi = 0; xi < dim; ++xi { for var xi = 0; xi < dim; ++xi {
let x = orig + Float(xi) * (sz + spacing) let x = orig + Float(xi) * (sz + spacing)
@@ -117,9 +159,13 @@ class BackgroundViewController: UIViewController {
for var zi = 0; zi < dim; ++zi { for var zi = 0; zi < dim; ++zi {
let z = orig + Float(zi) * (sz + spacing) let z = orig + Float(zi) * (sz + spacing)
let node = SCNNode(geometry: geom) let nodeGeom = geom.copy() as! SCNGeometry
nodeGeom.firstMaterial = material.copy() as? SCNMaterial
let node = SCNNode(geometry: nodeGeom)
node.position = SCNVector3Make(x, y, z) node.position = SCNVector3Make(x, y, z)
_cubletsNode.addChildNode(node) _cubletsNode.addChildNode(node)
_cublets.append(node)
} }
} }
} }
@@ -131,7 +177,7 @@ class BackgroundViewController: UIViewController {
internal func _setupAnimation() internal func _setupAnimation()
{ {
let rotAxis = SCNVector3Make(0.25, 1.75, 1.0) let rotAxis = SCNVector3Make(0.25, 1.75, 1.0)
let rotAngle = CGFloat(Float(2.0)*π) let rotAngle = CGFloat(Float(2.0) * π)
let rotAction = SCNAction.repeatActionForever(SCNAction.rotateByAngle(rotAngle, aroundAxis: rotAxis, duration: 40.0)) let rotAction = SCNAction.repeatActionForever(SCNAction.rotateByAngle(rotAngle, aroundAxis: rotAxis, duration: 40.0))
_cubletsNode.runAction(rotAction) _cubletsNode.runAction(rotAction)
} }
@@ -144,4 +190,10 @@ class BackgroundViewController: UIViewController {
_sceneView?.technique = technique _sceneView?.technique = technique
} }
internal func _setCubletActivated(cublet: SCNNode, activated: Bool)
{
let material = cublet.geometry?.firstMaterial
material?.diffuse.contents = (activated ? UIColor.redColor() : UIColor.whiteColor())
}
} }
@@ -11,6 +11,8 @@ import UIKit
class MainViewController: UIViewController { class MainViewController: UIViewController {
private var _backgroundController: BackgroundViewController = BackgroundViewController() private var _backgroundController: BackgroundViewController = BackgroundViewController()
// MARK: Overrides
override func viewDidLoad() override func viewDidLoad()
{ {
super.viewDidLoad() super.viewDidLoad()
@@ -19,6 +21,15 @@ class MainViewController: UIViewController {
self.addChildViewController(_backgroundController) self.addChildViewController(_backgroundController)
self.view.addSubview(_backgroundController.view) self.view.addSubview(_backgroundController.view)
let doubleTapGR = UITapGestureRecognizer(target: self, action: Selector("_handleDoubleFingerTap:"))
doubleTapGR.numberOfTouchesRequired = 2
self.view.addGestureRecognizer(doubleTapGR)
let singleTapGR = UITapGestureRecognizer(target: self, action: Selector("_handleSingleFingerTap:"))
singleTapGR.numberOfTouchesRequired = 1
singleTapGR.requireGestureRecognizerToFail(doubleTapGR)
self.view.addGestureRecognizer(singleTapGR)
} }
override func prefersStatusBarHidden() -> Bool override func prefersStatusBarHidden() -> Bool
@@ -33,4 +44,17 @@ class MainViewController: UIViewController {
let bounds = self.view.bounds let bounds = self.view.bounds
_backgroundController.view.frame = bounds _backgroundController.view.frame = bounds
} }
// MARK: Internal
internal func _handleSingleFingerTap(gestureRecognizer: UITapGestureRecognizer)
{
let randomPercentage = Float(arc4random()) / Float(UInt32.max)
_backgroundController.setPercentActivated(randomPercentage, animated: true)
}
internal func _handleDoubleFingerTap(gestureRecognizer: UITapGestureRecognizer)
{
_backgroundController.setPercentActivated(0.0, animated: true)
}
} }