-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathSceneNodeCreator.swift
More file actions
257 lines (222 loc) · 11.1 KB
/
SceneNodeCreator.swift
File metadata and controls
257 lines (222 loc) · 11.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
//
// SceneNodeCreator.swift
//
//
// Created by Ashis Laha on 14/07/17.
//
import Foundation
import SceneKit
enum GeometryNode {
case Box
case Pyramid
case Capsule
case Cone
case Cylinder
}
enum ArrowDirection {
case towards
case backwards
case left
case right
}
class SceneNodeCreator {
class func getGeometryNode(type : GeometryNode, position : SCNVector3, text : String? = nil, imageName : String? = nil) -> SCNNode {
var geometry : SCNGeometry!
switch type {
case .Box: geometry = SCNBox(width: 0.5, height: 0.5, length: 0.5, chamferRadius: 0.2)
case .Pyramid: geometry = SCNPyramid(width: 0.5, height: 0.5, length: 0.5)
case .Capsule: geometry = SCNCapsule(capRadius: 0.5, height: 0.5)
case .Cone: geometry = SCNCone(topRadius: 0.0, bottomRadius: 0.3, height: 0.5)
case .Cylinder: geometry = SCNCylinder(radius: 0.1, height: 0.5)
}
if let imgName = imageName , let image = UIImage(named: imgName) {
geometry.firstMaterial?.diffuse.contents = image
} else if let txt = text, let img = imageWithText(text:txt, imageSize: CGSize(width: 1024, height: 1024), backgroundColor: UIColor.getRandomColor()) {
geometry.firstMaterial?.diffuse.contents = img
} else {
geometry.firstMaterial?.diffuse.contents = UIColor.getRandomColor()
}
geometry.firstMaterial?.specular.contents = UIColor.getRandomColor()
let node = SCNNode(geometry: geometry)
node.position = position
return node
}
class func drawPath(position1 : SCNVector3, position2 : SCNVector3 ) -> SCNNode {
// calculate Angle
let dx = position2.x - position1.x
let dz = (-1.0) * (position2.z - position1.z)
var theta = atan(Double(dz/dx))
if theta == .nan {
theta = 3.14159265358979 / 2 // 90 Degree
}
print("Angle between point1 and point2 is : \(theta * 180 / Double.pi) along Y-Axis")
//Create Node
let width = CGFloat(sqrt( dx*dx + dz*dz ))
let height : CGFloat = 0.1
let length : CGFloat = 0.8
let chamferRadius : CGFloat = 0.05
let route = SCNBox(width: width, height: height, length: length, chamferRadius: chamferRadius)
route.firstMaterial?.diffuse.contents = UIColor(red: 210.0/255.0, green: 217.0/255.0, blue: 66.0/255.0, alpha: 1.0)
let midPosition = SCNVector3Make((position1.x+position2.x)/2, -1, (position1.z+position2.z)/2)
let node = SCNNode(geometry: route)
node.position = midPosition
// Do rotation of node in "theta" angle along Y-axis with Animation
/*
let rotation = CABasicAnimation(keyPath: "rotation")
rotation.fromValue = SCNVector4Make(0, 1, 0, 0)
rotation.toValue = SCNVector4Make(0, 1, 0, Float(theta))
rotation.duration = 2.0
node.addAnimation(rotation, forKey: "Rotate it")
*/
node.rotation = SCNVector4Make(0, 1, 0, Float(theta))
return node
}
class func drawArrow(position1 : SCNVector3, position2 : SCNVector3 ) -> SCNNode {
let angle = getAngle(position1: position1, position2: position2)
// create node
let midPosition = SCNVector3Make((position1.x+position2.x)/2, 1.0, (position1.z+position2.z)/2)
print("Draw Arrow at \(midPosition)")
let arrowNode = SceneNodeCreator.createNodeWithImage(image: UIImage(named: "arrow")!, position: midPosition, width: 2, height: 2)
arrowNode.rotation = SCNVector4Make(0, 1, 0, Float(angle))
return arrowNode
}
class func getAngle(position1 : SCNVector3, position2 : SCNVector3) -> Double {
// calculate Angle
let dx = position2.x - position1.x
let dz = (-1.0) * (position2.z - position1.z)
var theta = atan(Double(dz/dx))
if theta == .nan {
theta = 3.14159265358979 / 2 // 90 Degree
}
return theta
}
class func drawBanner(position1 : SCNVector3, position2 : SCNVector3) -> [SCNNode] { // it gives at the begining & mid-point for advertisement
let delta : Float = 5.0
let startPosition = SCNVector3Make(position1.x + delta, 1.0, position1.z + delta)
let midPosition = SCNVector3Make((position1.x+position2.x)/2 + delta, 1.0, (position1.z+position2.z)/2 + delta) // adding to x or Z should be based on angle
print("Advertisement drawn at begin : \(startPosition) and mid : \(midPosition)")
let bannerBegin = SceneNodeCreator.createNodeWithImage(image: UIImage(named: "advertisement")!, position: startPosition, width: 7, height: 7)
let bannerMid = SceneNodeCreator.createNodeWithImage(image: UIImage(named: "advertisement")!, position: midPosition, width: 7, height: 7)
return [bannerBegin,bannerMid]
}
class func createNodeWithImage(image : UIImage, position : SCNVector3 , width : CGFloat, height : CGFloat ) -> SCNNode {
let plane = SCNPlane(width: width, height: height)
plane.firstMaterial?.diffuse.contents = image
plane.firstMaterial?.lightingModel = .constant
let node = SCNNode(geometry: plane)
node.position = position
return node
}
// Composite node
class func getCompositeNode(position : SCNVector3 , direction : ArrowDirection ) -> SCNNode {
let color = UIColor.getRandomColor()
let cylinder = SCNCylinder(radius: 0.1, height: 0.6)
cylinder.firstMaterial?.diffuse.contents = color
let cylinderNode = SCNNode(geometry: cylinder)
let pyramid = SCNPyramid(width: 0.5, height: 0.5, length: 0.5)
pyramid.firstMaterial?.diffuse.contents = color
let pyramidNode = SCNNode(geometry: pyramid)
pyramidNode.position = position
pyramidNode.addChildNode(cylinderNode)
let rotation = CABasicAnimation(keyPath: "rotation")
switch direction {
case .left:
rotation.fromValue = SCNVector4Make(0, 0, 1, 0)
rotation.toValue = SCNVector4Make(0, 0, 1, Float(Double.pi / 2 )) // Anti-clockwise 90 degree around z-axis
pyramidNode.rotation = SCNVector4Make(0, 0, 1, Float(Double.pi / 2 ))
case .right:
rotation.fromValue = SCNVector4Make(0, 0, 1, 0)
rotation.toValue = SCNVector4Make(0, 0, 1, -Float(Double.pi / 2 )) // clockwise 90 degree around z-axis
pyramidNode.rotation = SCNVector4Make(0, 0, 1, -Float(Double.pi / 2 ))
case .towards:
rotation.fromValue = SCNVector4Make(1, 0, 0, 0)
rotation.toValue = SCNVector4Make(1, 0, 0, -Float(Double.pi / 2 )) // clockwise 90 degree around x-axis
pyramidNode.rotation = SCNVector4Make(1, 0, 0, -Float(Double.pi / 2 ))
case .backwards:
rotation.fromValue = SCNVector4Make(1, 0, 0, 0)
rotation.toValue = SCNVector4Make(1, 0, 0, Float(Double.pi / 2 )) // anti-clockwise 90 degree around x-axis
pyramidNode.rotation = SCNVector4Make(1, 0, 0, Float(Double.pi / 2 ))
}
rotation.duration = 2.0
pyramidNode.addAnimation(rotation, forKey: "Rotate it")
return pyramidNode
}
// Camera node
class func createCameraNode(position : SCNVector3) -> SCNNode {
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = position
return cameraNode
}
// omni light node
class func createLightNode(position : SCNVector3) -> SCNNode {
let omniLightNode = SCNNode()
omniLightNode.light = SCNLight()
omniLightNode.light?.type = .omni
omniLightNode.light?.color = UIColor.white.withAlphaComponent(0.5)
omniLightNode.position = position
return omniLightNode
}
// Scene node
class func createSceneNode(sceneName : String , position : SCNVector3) -> SCNNode {
if let scene = SCNScene(named:sceneName) {
let sceneNode = scene.rootNode.childNodes.first ?? SCNNode()
sceneNode.position = position
return sceneNode
}
return SCNNode()
}
// Image with Text
class func imageWithText(text:String, fontSize:CGFloat = 150, fontColor: UIColor = .black, imageSize:CGSize, backgroundColor:UIColor) -> UIImage? {
let imageRect = CGRect(origin: CGPoint.zero, size: imageSize)
UIGraphicsBeginImageContext(imageSize)
defer { UIGraphicsEndImageContext() }
guard let context = UIGraphicsGetCurrentContext() else { return nil }
// Fill the background with a color
context.setFillColor(backgroundColor.cgColor)
context.fill(imageRect)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .center
// Define the attributes of the text
let attributes : [NSAttributedStringKey : Any] = [
NSAttributedStringKey.font : UIFont(name: "TimesNewRomanPS-BoldMT", size:fontSize) ?? UIFont.italicSystemFont(ofSize: fontSize),
NSAttributedStringKey.paragraphStyle : paragraphStyle,
NSAttributedStringKey.foregroundColor : fontColor
]
// Determine the width/height of the text for the attributes
let textSize = text.size(withAttributes: attributes)
// Draw text in the current context
text.draw(at: CGPoint(x: imageSize.width/2 - textSize.width/2, y: imageSize.height/2 - textSize.height/2), withAttributes: attributes)
if let image = UIGraphicsGetImageFromCurrentImageContext() {
return image
}
return nil
}
// Temporary Scene Graph
class func sceneSetUp() -> SCNScene {
let scene = SCNScene()
scene.rootNode.addChildNode(SceneNodeCreator.getGeometryNode(type: .Box, position: SCNVector3Make(-2, 0, -1), text: "Hi"))
scene.rootNode.addChildNode(SceneNodeCreator.getGeometryNode(type: .Capsule, position: SCNVector3Make(-1, 0, -1), text: "Hi" ))
scene.rootNode.addChildNode(SceneNodeCreator.getCompositeNode(position: SCNVector3Make(0, 0, -2), direction: .right))
scene.rootNode.addChildNode(SceneNodeCreator.createSceneNode(sceneName: "art.scnassets/ship.scn", position: SCNVector3Make(1, 0, -1)))
scene.rootNode.addChildNode(SceneNodeCreator.getGeometryNode(type: .Cone, position: SCNVector3Make(2, 0, -1),text: "Hi"))
scene.rootNode.addChildNode(SceneNodeCreator.getGeometryNode(type: .Pyramid, position: SCNVector3Make(3, 0, -1),text: "Hi"))
return scene
}
}
extension UIColor {
class func getRandomColor() -> UIColor {
let random = Int(arc4random_uniform(8))
switch random {
case 0: return UIColor.red
case 1: return UIColor.brown
case 2: return UIColor.green
case 3: return UIColor.yellow
case 4: return UIColor.blue
case 5: return UIColor.purple
case 6: return UIColor.cyan
case 7: return UIColor.orange
default: return UIColor.darkGray
}
}
}