Skip to content

Commit

Permalink
Added cone and cylinder geometries
Browse files Browse the repository at this point in the history
  • Loading branch information
maxxfrazer committed Jun 12, 2021
1 parent b1b86ef commit 19c4582
Show file tree
Hide file tree
Showing 4 changed files with 321 additions and 6 deletions.
38 changes: 38 additions & 0 deletions Sources/RealityGeometries/CompleteVertex.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// File.swift
//
//
// Created by Max Cobb on 12/06/2021.
//

import RealityKit

internal struct CompleteVertex {
var position: SIMD3<Float>
var normal: SIMD3<Float>
var uv: SIMD2<Float>
}

internal extension Array where Element == CompleteVertex {
func generateMeshDescriptor(
with indices: [UInt32], materials: [UInt32] = []
) -> MeshDescriptor {
var meshDescriptor = MeshDescriptor()
var positions: [SIMD3<Float>] = []
var normals: [SIMD3<Float>] = []
var uvs: [SIMD2<Float>] = []
for vx in self {
positions.append(vx.position)
normals.append(vx.normal)
uvs.append(vx.uv)
}
meshDescriptor.positions = MeshBuffers.Positions(positions)
meshDescriptor.normals = MeshBuffers.Normals(normals)
meshDescriptor.textureCoordinates = MeshBuffers.TextureCoordinates(uvs)
meshDescriptor.primitives = .triangles(indices)
if !materials.isEmpty {
meshDescriptor.materials = MeshDescriptor.Materials.perFace(materials)
}
return meshDescriptor
}
}
125 changes: 125 additions & 0 deletions Sources/RealityGeometries/MeshResource+Cone.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
//
// File.swift
//
//
// Created by Max Cobb on 12/06/2021.
//

import RealityKit

extension MeshResource {
fileprivate static func coneIndices(_ sides: Int, _ lowerCenterIndex: UInt32, _ splitFaces: Bool) -> ([UInt32], [UInt32]) {
var indices: [UInt32] = []
var materialIndices: [UInt32] = []
for side in 0..<sides {
let bottomLeft = UInt32(side)
let bottomRight = UInt32(side + 1)
let topVertex = UInt32(side + sides + 1)

// First triangle of side
indices.append(contentsOf: [bottomLeft, topVertex, bottomRight])

// Add bottom cap triangle
indices.append(contentsOf: [0, UInt32(side) + 1, UInt32(side) + 2].map { $0 + lowerCenterIndex })

if splitFaces {
materialIndices.append(0)
materialIndices.append(1)
}
}
return (indices, materialIndices)
}

fileprivate static func coneVertices(
_ sides: Int, _ radius: Float, _ height: Float
) -> ([CompleteVertex], [CompleteVertex], [CompleteVertex]) {
var theta: Float = 0
let thetaInc = 2 * .pi / Float(sides)
let uStep: Float = 1 / Float(sides);
// first vertices added will be bottom edges
var vertices = [CompleteVertex]()
// all top edge vertices of the cylinder
var upperEdgeVertices = [CompleteVertex]()
// bottom edge vertices
var lowerCapVertices = [CompleteVertex]()

// create vertices for all sides of the cylinder
for side in 0...sides {
let cosTheta = cos(theta)
let sinTheta = sin(theta)

let lowerPosition: SIMD3<Float> = [
radius * cosTheta, -height / 2, radius * sinTheta
]

let lowerNormal = cross(
SIMD3<Float>(-sinTheta, 0, cosTheta),
SIMD3<Float>(0, 1, 0) - SIMD3<Float>(cosTheta, 0, sinTheta)
)
let bottomVertex = CompleteVertex(
position: lowerPosition,
normal: lowerNormal,
uv: [uStep * Float(side), 0]
)

// add vertex for bottom side of cylinder, facing out
vertices.append(bottomVertex)

// add vertex for bottom side facing down
lowerCapVertices.append(CompleteVertex(
position: bottomVertex.position,
normal: [0, -1, 0], uv: [cosTheta + 1, sinTheta + 1] / 2)
)

// add vertex for top side facing out
let topVertex = CompleteVertex(
position: [0, height / 2, 0],
normal: lowerNormal, uv: [0.5, 1]
)
upperEdgeVertices.append(topVertex)

theta += thetaInc;
}
return (vertices, upperEdgeVertices, lowerCapVertices)
}

/// Creates a new cone mesh with the specified values
/// - Parameters:
/// - radius: Radius of the code base
/// - height: Height of the code from base to tip
/// - sides: How many sides the cone should have, default is 24, minimum is 3
/// - splitFaces: A Boolean you set to true to indicate that vertices shouldn’t be merged.
/// - Returns: A cone mesh
public static func generateCone(
radius: Float, height: Float, sides: Int = 24, splitFaces: Bool = false
) throws -> MeshResource {
assert(sides > 2, "Sides must be an integer above 2")
let halfHeight = height / 2

// first vertices added to vertices will be bottom edges
// upperEdgeVertices are all top edge vertices of the cylinder
// lowerCapVertices are the bottom edge vertices
var (
vertices, upperEdgeVertices, lowerCapVertices
) = coneVertices(sides, radius, height)

vertices.append(contentsOf: upperEdgeVertices)

let lowerCenterIndex = UInt32(vertices.count)
vertices.append(CompleteVertex(
position: [0, -halfHeight, 0], normal: [0, -1, 0], uv: [0.5, 0.5]
))

vertices.append(contentsOf: lowerCapVertices)

let (indices, materialIndices) = coneIndices(
sides, lowerCenterIndex, splitFaces
)

let meshDescr = vertices.generateMeshDescriptor(
with: indices, materials: materialIndices
)
return try MeshResource.generate(from: [meshDescr])
}
}

143 changes: 143 additions & 0 deletions Sources/RealityGeometries/MeshResource+Cylinder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
//
// File.swift
//
//
// Created by Max Cobb on 12/06/2021.
//

import RealityKit

extension MeshResource {
fileprivate static func cylinderIndices(_ sides: Int, _ lowerCenterIndex: UInt32, _ upperCenterIndex: UInt32, _ splitFaces: Bool) -> ([UInt32], [UInt32]) {
var indices: [UInt32] = []
var materialIndices: [UInt32] = []
for side in 0..<sides {
let bottomLeft = UInt32(side)
let bottomRight = UInt32(side + 1)
let topLeft = UInt32(side + sides + 1)
let topRight = UInt32(side + sides + 2)

// First triangle of side
indices.append(contentsOf: [bottomLeft, topRight, bottomRight])

// Second triangle of side
indices.append(contentsOf: [bottomLeft, topLeft, topRight])

// Add bottom cap triangle
indices.append(contentsOf: [0, UInt32(side) + 1, UInt32(side) + 2].map { $0 + lowerCenterIndex })

// Add top cap triangle
indices.append(contentsOf: [0, UInt32(side) + 2, UInt32(side) + 1].map { $0 + upperCenterIndex })
if splitFaces {
materialIndices.append(0)
materialIndices.append(0)
materialIndices.append(1)
materialIndices.append(2)
}
}
return (indices, materialIndices)
}

fileprivate static func cylinderVertices(
_ sides: Int, _ radius: Float, _ height: Float
) -> ([CompleteVertex], [CompleteVertex], [CompleteVertex], [CompleteVertex]) {
var theta: Float = 0
let normalizeMult = 1 / sqrt(radius)
let thetaInc = 2 * .pi / Float(sides)
let uStep: Float = 1 / Float(sides);
// first vertices added will be bottom edges
var vertices = [CompleteVertex]()
// all top edge vertices of the cylinder
var upperEdgeVertices = [CompleteVertex]()
// bottom edge vertices
var lowerCapVertices = [CompleteVertex]()
// top edge vertices
var upperCapVertices = [CompleteVertex]()

// create vertices for all sides of the cylinder
for side in 0...sides {
let cosTheta = cos(theta)
let sinTheta = sin(theta)

let lowerPosition: SIMD3<Float> = [
radius * cosTheta, -height / 2, radius * sinTheta
]

let bottomVertex = CompleteVertex(
position: lowerPosition,
normal: [lowerPosition.x, 0, lowerPosition.z] * normalizeMult,
uv: [uStep * Float(side), 0]
)

// add vertex for bottom side of cylinder, facing out
vertices.append(bottomVertex)

// add vertex for bottom side facing down
lowerCapVertices.append(CompleteVertex(
position: bottomVertex.position,
normal: [0, -1, 0], uv: [cosTheta + 1, sinTheta + 1] / 2)
)

// add vertex for top side facing out
let topVertex = CompleteVertex(
position: bottomVertex.position + [0, height, 0],
normal: bottomVertex.normal, uv: [uStep * Float(side), 1]
)
upperEdgeVertices.append(topVertex)

upperCapVertices.append(CompleteVertex(
position: topVertex.position,
normal: [0, 1, 0], uv: [cosTheta + 1, sinTheta + 1] / 2)
)

theta += thetaInc;
}
return (vertices, upperEdgeVertices, lowerCapVertices, upperCapVertices)
}

/// Creates a new cylinder mesh with the specified values
/// - Parameters:
/// - radius: Radius of the cylinder
/// - height: Height of the cylinder
/// - sides: How many sides the cone should have, default is 24, minimum is 3
/// - splitFaces: A Boolean you set to true to indicate that vertices shouldn’t be merged.
/// - Returns: A cylinder mesh.
public static func generateCylinder(
radius: Float, height: Float, sides: Int = 24, splitFaces: Bool = false
) throws -> MeshResource {
assert(sides > 2, "Sides must be an integer above 2")

let halfHeight = height / 2

// first vertices added to vertices will be bottom edges
// upperEdgeVertices are all top edge vertices of the cylinder
// lowerCapVertices are the bottom edge vertices
// upperCapVertices are the top edge vertices
var (
vertices, upperEdgeVertices, lowerCapVertices, upperCapVertices
) = cylinderVertices(sides, radius, height)

vertices.append(contentsOf: upperEdgeVertices)

let lowerCenterIndex = UInt32(vertices.count)
vertices.append(CompleteVertex(
position: [0, -halfHeight, 0], normal: [0, -1, 0], uv: [0.5, 0.5]
))

vertices.append(contentsOf: lowerCapVertices)
let upperCenterIndex = UInt32(vertices.count)
vertices.append(CompleteVertex(
position: [0, halfHeight, 0], normal: [0, 1, 0], uv: [0.5, 0.5]
))
vertices.append(contentsOf: upperCapVertices)

let (indices, materialIndices) = cylinderIndices(
sides, lowerCenterIndex, upperCenterIndex, splitFaces
)

let meshDescr = vertices.generateMeshDescriptor(
with: indices, materials: materialIndices
)
return try MeshResource.generate(from: [meshDescr])
}
}
21 changes: 15 additions & 6 deletions Sources/RealityGeometries/MeshResource+Planes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,29 @@
import RealityKit

extension MeshResource {
static func generateDetailedPlane(
/// Creates a new plane mesh with the specified values.
/// - Parameters:
/// - width: Width of the output plane
/// - depth: Depth of the output plane
/// - vertices: Vertex count in the x and z axis
/// - Returns: A plane mesh
public static func generateDetailedPlane(
width: Float, depth: Float, vertices: (Int, Int)
) throws -> MeshResource {
var descr = MeshDescriptor()
var meshPositions: [SIMD3<Float>] = []
var indices: [UInt32] = []
for x_v in 0..<(vertices.0) { // 5
var textureMap: [SIMD2<Float>] = []
for x_v in 0..<(vertices.0) {
let vertexCounts = meshPositions.count
print(vertexCounts)
for y_v in 0..<(vertices.1) { // 4
for y_v in 0..<(vertices.1) {
meshPositions.append([
(Float(x_v) / Float(vertices.0 - 1) - 0.5) * width, 0, (0.5 - Float(y_v) / Float(vertices.1 - 1)) * depth
(Float(x_v) / Float(vertices.0 - 1) - 0.5) * width,
0,
(0.5 - Float(y_v) / Float(vertices.1 - 1)) * depth
])
textureMap.append([Float(x_v) / Float(vertices.0 - 1), Float(y_v) / Float(vertices.1 - 1)])
if x_v > 0 && y_v > 0 {
indices.append(
contentsOf: [
Expand All @@ -32,8 +42,7 @@ extension MeshResource {
}
descr.primitives = .triangles(indices)
descr.positions = MeshBuffer(meshPositions)
// - TODO: Add Texture Map
// descr.textureCoordinates = MeshBuffers.TextureCoordinates([])
descr.textureCoordinates = MeshBuffers.TextureCoordinates(textureMap)
return try .generate(from: [descr])
}
}

0 comments on commit 19c4582

Please sign in to comment.