Skip to content

Commit

Permalink
added comments, cleaned some of the code, added more documentation an…
Browse files Browse the repository at this point in the history
…d examples in the readme
  • Loading branch information
maxxfrazer committed Dec 19, 2018
1 parent f052924 commit fdd41ff
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 37 deletions.
15 changes: 10 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# ARKit-SCNPathNode
# ARKit-SCNPath

Navigation seems to be a strong point for people making AR apps. So here's a class to easily create a path in AR along a set of centre points.

I'll be releasing a full tutorial shortly after Xmas on my [Medum page](https://medium.com/@maxxfrazer) on how I made the examples in the below gifs using this Pod and about 30 lines of non boilerplate code.

Please feel free to use and contribute this library however you like.
I only ask that you let me know when you're doing so, so I can see some cool uses of it!

It's as easy as this to make a path:
It's as easy as this to make a node with this path as a geometry:
```
let pathNode = SCNPathNode(path: [
SCNVector3(0,-1,0),
Expand All @@ -14,8 +16,11 @@ let pathNode = SCNPathNode(path: [
])
```

The y value is set to -1 just as an example that assumes the origin of your scene graph is 1m above the ground. Use plane detection to actually hit the ground correctly.


<!-- Here's some basic examples of what you can do with this Pod: -->
Here's some basic examples of what you can do with this Pod:

<!-- ![Path Example 1](https://github.com/maxxfrazer/ARKit-SCNPathNode/blob/master/media/SCNPathNode-example1.gif) -->
<!-- ![Path Example 2](https://github.com/maxxfrazer/ARKit-SCNPathNode/blob/master/media/SCNPathNode-example2.gif) -->
![Path Example 1](https://github.com/maxxfrazer/ARKit-SCNPathNode/blob/master/media/path-example-1.gif)
![Path Example Texture Repeating](https://github.com/maxxfrazer/ARKit-SCNPathNode/blob/master/media/path-example-2.gif)
![Path Example Creating](https://github.com/maxxfrazer/ARKit-SCNPathNode/blob/master/media/path-example-3.gif)
8 changes: 4 additions & 4 deletions SCNPath.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
008A74DF21C5879D0066FB87 /* SCNPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 008A74DC21C5879D0066FB87 /* SCNPath.swift */; };
008A74DF21C5879D0066FB87 /* SCNPathNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 008A74DC21C5879D0066FB87 /* SCNPathNode.swift */; };
008A74E021C5879D0066FB87 /* SCNGeometry+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 008A74DD21C5879D0066FB87 /* SCNGeometry+Extensions.swift */; };
008A74E121C5879D0066FB87 /* SCNVector3+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 008A74DE21C5879D0066FB87 /* SCNVector3+Extensions.swift */; };
/* End PBXBuildFile section */
Expand All @@ -26,7 +26,7 @@

/* Begin PBXFileReference section */
0047D21521BB0703006E60A8 /* libSCNPath.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libSCNPath.a; sourceTree = BUILT_PRODUCTS_DIR; };
008A74DC21C5879D0066FB87 /* SCNPath.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SCNPath.swift; sourceTree = "<group>"; };
008A74DC21C5879D0066FB87 /* SCNPathNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SCNPathNode.swift; sourceTree = "<group>"; };
008A74DD21C5879D0066FB87 /* SCNGeometry+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SCNGeometry+Extensions.swift"; sourceTree = "<group>"; };
008A74DE21C5879D0066FB87 /* SCNVector3+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SCNVector3+Extensions.swift"; sourceTree = "<group>"; };
/* End PBXFileReference section */
Expand Down Expand Up @@ -62,7 +62,7 @@
isa = PBXGroup;
children = (
008A74DD21C5879D0066FB87 /* SCNGeometry+Extensions.swift */,
008A74DC21C5879D0066FB87 /* SCNPath.swift */,
008A74DC21C5879D0066FB87 /* SCNPathNode.swift */,
008A74DE21C5879D0066FB87 /* SCNVector3+Extensions.swift */,
);
path = SCNPath;
Expand Down Expand Up @@ -150,7 +150,7 @@
files = (
008A74E021C5879D0066FB87 /* SCNGeometry+Extensions.swift in Sources */,
008A74E121C5879D0066FB87 /* SCNVector3+Extensions.swift in Sources */,
008A74DF21C5879D0066FB87 /* SCNPath.swift in Sources */,
008A74DF21C5879D0066FB87 /* SCNPathNode.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
16 changes: 9 additions & 7 deletions SCNPath/SCNGeometry+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,28 +96,28 @@ public extension SCNGeometry {
var texutreCoords: [CGPoint] = []
let maxIndex = path.count - 1
var directionV = SCNVector3Zero
// var addToPoint: SCNVector3
var angleBent: Float?
var bentBy: Float = 0
for (index, vert) in path.enumerated() {
if index == 0 {
// first point
directionV = SCNVector3(path[index + 1].z - vert.z, 0, vert.x - path[index + 1].x)
} else if index < maxIndex {
let toThis = (vert - path[index - 1]).flattened().normalized()
let fromThis = (path[index + 1] - vert).flattened().normalized()
angleBent = fromThis.angleChange(to: toThis)
bentBy = fromThis.angleChange(to: toThis)
let resultant = (toThis + fromThis) / 2
directionV = SCNVector3(resultant.z, 0, -resultant.x)
} else {
// last point
directionV = SCNVector3(vert.z - path[index - 1].z, 0, path[index - 1].x - vert.x)
}
let addToPoint = directionV.normalized() * (width / 2)
if curvePoints > 0, path.count >= index + 2, var bentBy = angleBent {
if curvePoints > 0, path.count >= index + 2, bentBy > 0.001 {
let edge1 = vert - addToPoint
let edge2 = vert + addToPoint
var bendAround = vert - (addToPoint * curveDistance)

// replace this with quaternions when possible
if newTurning(points: Array(path[(index-1)...(index+1)])) < 0 { // left turn
bendAround = vert + (addToPoint * curveDistance)
bentBy *= -1
Expand All @@ -128,14 +128,15 @@ public extension SCNGeometry {
vertices.append(edge1.rotate(
about: bendAround, by: (-0.5 + Float(val) / curvePoints) * bentBy))
addTriangleIndices(indices: &indices, at: UInt32(vertices.count - 2))
vertices.append(contentsOf: vertices[(vertices.count - 2)...])
// When the normals are added properly, uncomment this line and the same below
// vertices.append(contentsOf: vertices[(vertices.count - 2)...])
}
} else {
vertices.append(vert + addToPoint)
vertices.append(vert - addToPoint)
if index > 0 {
addTriangleIndices(indices: &indices, at: UInt32(vertices.count - 2))
vertices.append(contentsOf: vertices[(vertices.count - 2)...])
// vertices.append(contentsOf: vertices[(vertices.count - 2)...])
}
}
}
Expand All @@ -148,7 +149,8 @@ public extension SCNGeometry {
let src = SCNGeometrySource(vertices: vertices)
let textureMap = SCNGeometrySource(textureCoordinates: texutreCoords)

// assuming the path is just flat for now, even though it can be angled
// assuming the path is just flat for now, even though it can be angled.
// the turning part doesn't do anything nice with sloped paths yet.
let norm = SCNGeometrySource(normals: [SCNVector3](
repeating: SCNVector3(0, 1, 0), count: vertices.count
))
Expand Down
49 changes: 28 additions & 21 deletions SCNPath/SCNPath.swift → SCNPath/SCNPathNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,36 +10,61 @@ import SceneKit

/// Subclass of SCNNode, when created holds a geometry representing the path.
public class SCNPathNode: SCNNode {

/// The centre points of the path to be drawn
public var path: [SCNVector3] {
// later this will be a little smarter, calculating the diff from the oldValue
didSet {
self.recalcGeometry()
}
}
/// The length of the path with the curvature of any turning points
public private(set) var pathLength: CGFloat = 0

/// Width of the path in meters
public var width: Float {
didSet {
if self.geometry != nil {
self.recalcGeometry()
}
}
}
private var textureRepeating: Float = 0

/// Whenever a curve is met, how many segments are wanted to curve the corner.
/// This will be a maximum in a later version, so slight bends don't create unecessary vertices.
public var curvePoints: Float {
didSet {
if self.geometry != nil {
self.recalcGeometry()
}
}
}

/// An array of SCNMaterial objects that determine the geometry’s appearance when rendered.
public var materials: [SCNMaterial] {
didSet {
if let geom = self.geometry {
let img = self.materials.first?.diffuse.contents
geom.materials = materials
}
}
}

/// If the texture is a seamless repeating image use this to say how tall the image should
/// be if the width = 1.
///
/// - Parameter meters: meters tall the image would be if the width = 1m.
public var textureRepeats = false {
didSet {
if textureRepeats != oldValue {
if textureRepeats {
self.recalcTextureScale()
} else {
self.resetTextureScale()
}
}
}
}

/// Create the SCNPathNode with the geometry and materials applied.
///
/// - Parameters:
Expand All @@ -60,22 +85,6 @@ public class SCNPathNode: SCNNode {
self.recalcGeometry()
}

/// If the texture is a seamless repeating image use this to say how tall the image should
/// be if the width = 1.
///
/// - Parameter meters: meters tall the image would be if the width = 1m.
public var textureRepeats = false {
didSet {
if textureRepeats != oldValue {
if textureRepeats {
self.recalcTextureScale()
} else {
self.resetTextureScale()
}
}
}
}

private func resetTextureScale() {
let contentsTransform = SCNMatrix4Scale(SCNMatrix4Identity, 1, 1, 1)
self.materials.first?.diffuse.contentsTransform = contentsTransform
Expand All @@ -95,12 +104,10 @@ public class SCNPathNode: SCNNode {
}

private func recalcGeometry() {
let (geom, length) = SCNGeometry.path(
(self.geometry, self.pathLength) = SCNGeometry.path(
path: path, width: self.width,
curvePoints: curvePoints, materials: self.materials
)
self.geometry = geom
self.pathLength = length
if self.textureRepeats {
self.recalcTextureScale()
} else {
Expand Down
Binary file added media/path-example-1.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added media/path-example-2.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added media/path-example-3.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit fdd41ff

Please sign in to comment.