diff --git a/Sources/PixarUSD/Base/Gf/MathInline.swift b/Sources/PixarUSD/Base/Gf/MathInline.swift index e17a78b7d..646dd01fb 100644 --- a/Sources/PixarUSD/Base/Gf/MathInline.swift +++ b/Sources/PixarUSD/Base/Gf/MathInline.swift @@ -20,7 +20,7 @@ public extension Gf /// - Parameter epsilon: The tolerance to use. /// /// - Returns: `true` if the values are within **epsilon** of each other. - @inline(__always) + @inlinable static func isClose(_ a: Double, _ b: Double, within epsilon: Double) -> Bool { Pixar.GfIsClose(a, b, epsilon) @@ -30,7 +30,7 @@ public extension Gf /// - Parameter radians: The angle in radians. /// /// - Returns: The angle in degrees. - @inline(__always) + @inlinable static func radiansToDegrees(angle radians: Double) -> Double { Pixar.GfRadiansToDegrees(radians) @@ -40,7 +40,7 @@ public extension Gf /// - Parameter radians: The angle in degrees. /// /// - Returns: The angle in radians. - @inline(__always) + @inlinable static func degreesToRadians(angle degrees: Double) -> Double { Pixar.GfDegreesToRadians(degrees) @@ -50,7 +50,7 @@ public extension Gf /// Returns the inner product of **x** with itself: specifically, `x * x`. /// Defined for **Int**, **Float**, **Double**, and all **GfVec** types. - @inline(__always) + @inlinable static func sqr(_ x: Int) -> Double { Pixar.GfSqr(x) @@ -58,7 +58,7 @@ public extension Gf /// Returns the inner product of **x** with itself: specifically, `x * x`. /// Defined for **Int**, **Float**, **Double**, and all **GfVec** types. - @inline(__always) + @inlinable static func sqr(_ x: Float) -> Double { Pixar.GfSqr(x) @@ -66,7 +66,7 @@ public extension Gf /// Returns the inner product of **x** with itself: specifically, `x * x`. /// Defined for **Int**, **Float**, **Double**, and all **GfVec** types. - @inline(__always) + @inlinable static func sqr(_ x: Double) -> Double { Pixar.GfSqr(x) @@ -74,7 +74,7 @@ public extension Gf /// Returns the inner product of **x** with itself: specifically, `x * x`. /// Defined for **Int**, **Float**, **Double**, and all **GfVec** types. - @inline(__always) + @inlinable static func sqr(_ x: GfVec2f) -> Double { Pixar.GfSqr(x) @@ -82,161 +82,161 @@ public extension Gf /// Returns the inner product of **x** with itself: specifically, `x * x`. /// Defined for **Int**, **Float**, **Double**, and all **GfVec** types. - @inline(__always) + @inlinable static func sqr(_ x: GfVec3f) -> Double { Pixar.GfSqr(x) } /// Return sqrt(f). - @inline(__always) + @inlinable static func sqrt(_ f: Double) -> Double { Pixar.GfSqrt(f) } /// Return sqrt(f). - @inline(__always) + @inlinable static func sqrt(_ f: Float) -> Float { Pixar.GfSqrt(f) } /// Return exp(f). - @inline(__always) + @inlinable static func exp(_ f: Double) -> Double { Pixar.GfExp(f) } /// Return exp(f). - @inline(__always) + @inlinable static func exp(_ f: Float) -> Float { Pixar.GfExp(f) } /// Return log(f). - @inline(__always) + @inlinable static func log(_ f: Double) -> Double { Pixar.GfLog(f) } /// Return log(f). - @inline(__always) + @inlinable static func log(_ f: Float) -> Float { Pixar.GfLog(f) } /// Return floor(f). - @inline(__always) + @inlinable static func floor(_ f: Double) -> Double { Pixar.GfFloor(f) } /// Return floor(f). - @inline(__always) + @inlinable static func floor(_ f: Float) -> Float { Pixar.GfFloor(f) } /// Return ceil(f). - @inline(__always) + @inlinable static func ceil(_ f: Double) -> Double { Pixar.GfCeil(f) } /// Return ceil(f). - @inline(__always) + @inlinable static func ceil(_ f: Float) -> Float { Pixar.GfCeil(f) } /// Return abs(f). - @inline(__always) + @inlinable static func abs(_ f: Double) -> Double { Pixar.GfAbs(f) } /// Return abs(f). - @inline(__always) + @inlinable static func abs(_ f: Float) -> Float { Pixar.GfAbs(f) } /// Return round(f). - @inline(__always) + @inlinable static func round(_ f: Double) -> Double { Pixar.GfRound(f) } /// Return round(f). - @inline(__always) + @inlinable static func round(_ f: Float) -> Float { Pixar.GfRound(f) } /// Return pow(f, p). - @inline(__always) + @inlinable static func pow(_ f: Double, _ p: Double) -> Double { Pixar.GfPow(f, p) } /// Return pow(f, p). - @inline(__always) + @inlinable static func pow(_ f: Float, _ p: Float) -> Float { Pixar.GfPow(f, p) } /// Return sin(v). - @inline(__always) + @inlinable static func sin(_ v: Double) -> Double { Pixar.GfSin(v) } /// Return sin(v). - @inline(__always) + @inlinable static func sin(_ v: Float) -> Float { Pixar.GfSin(v) } /// Return cos(v). - @inline(__always) + @inlinable static func cos(_ v: Double) -> Double { Pixar.GfCos(v) } /// Return cos(v). - @inline(__always) + @inlinable static func cos(_ v: Float) -> Float { Pixar.GfCos(v) } /// Return sin(v) in s and cos(v) in c. - @inline(__always) + @inlinable static func sinCos(_ v: Double, _ s: inout Double, _ c: inout Double) { Pixar.GfSinCos(v, &s, &c) } /// Return sin(v) in s and cos(v) in c. - @inline(__always) + @inlinable static func sinCos(_ v: Float, _ s: inout Float, _ c: inout Float) { Pixar.GfSinCos(v, &s, &c) @@ -244,7 +244,7 @@ public extension Gf /// Return the resulting of clamping value to lie between /// min and max. This function is also defined for GfVecs. - @inline(__always) + @inlinable static func clamp(_ value: Double, _ min: Double, _ max: Double) -> Double { Pixar.GfClamp(value, min, max) @@ -252,7 +252,7 @@ public extension Gf /// Return the resulting of clamping value to lie between /// min and max. This function is also defined for GfVecs. - @inline(__always) + @inlinable static func clamp(_ value: Float, _ min: Float, _ max: Float) -> Float { Pixar.GfClamp(value, min, max) @@ -263,7 +263,7 @@ public extension Gf /// If a = `n b` for some integer n, zero is returned. /// Otherwise, for positive a, the value returned is `fmod(a,b)`, /// and for negative `a`, the value returned is `fmod(a,b) + b`. - @inline(__always) + @inlinable static func mod(_ a: Double, _ b: Double) -> Double { Pixar.GfMod(a, b) @@ -274,7 +274,7 @@ public extension Gf /// If a = `n b` for some integer n, zero is returned. /// Otherwise, for positive a, the value returned is `fmod(a,b)`, /// and for negative `a`, the value returned is `fmod(a,b) + b`. - @inline(__always) + @inlinable static func mod(_ a: Float, _ b: Float) -> Float { Pixar.GfMod(a, b) diff --git a/Sources/PixarUSD/Base/Gf/Matrix4d.swift b/Sources/PixarUSD/Base/Gf/Matrix4d.swift index cc8d81b65..ae1284448 100644 --- a/Sources/PixarUSD/Base/Gf/Matrix4d.swift +++ b/Sources/PixarUSD/Base/Gf/Matrix4d.swift @@ -39,19 +39,33 @@ extension Gf.Matrix4d: Scalar /// Axis count of the vector. public typealias AxisCount = Axis16 - public func getRow(_ index: Int) -> Gf.Vec4d + /// Sets a row of the matrix from a ```Gf.Vec4d```. + public mutating func setRow(at rowIndex: Int, to value: Gf.Vec4d) { - GetRow(Int32(index)) + SetRow(Int32(rowIndex), value) } - public func getColumn(_ index: Int) -> Gf.Vec4d + /// Sets a column of the matrix from a ```Gf.Vec4d```. + public mutating func setColumn(at columnIndex: Int, to value: Gf.Vec4d) { - GetColumn(Int32(index)) + SetColumn(Int32(columnIndex), value) } - /// Sets the matrix from 16 independent \c double values, - /// specified in row-major order. For example, parameter \e m10 specifies - /// the value in row 1 and column 0. + /// Gets a row of the matrix as a ```Gf.Vec4```. + public func getRow(at rowIndex: Int) -> Gf.Vec4d + { + GetRow(Int32(rowIndex)) + } + + /// Gets a column of the matrix as a ```Gf.Vec4```. + public func getColumn(at columnIndex: Int) -> Gf.Vec4d + { + GetColumn(Int32(columnIndex)) + } + + /// Sets the matrix from 16 independent `Double` values, + /// specified in row-major order. For example, parameter + /// `m10` specifies the value in row 1 and column 0. public mutating func set(_ m00: Double, _ m01: Double, _ m02: Double, _ m03: Double, _ m10: Double, _ m11: Double, _ m12: Double, _ m13: Double, _ m20: Double, _ m21: Double, _ m22: Double, _ m23: Double, _ m30: Double, _ m31: Double, _ m32: Double, @@ -63,7 +77,7 @@ extension Gf.Matrix4d: Scalar m33).pointee } - /// Get the elements of this Matrix as an array. + /// Get the elements of this matrix as an array. public func getArray() -> [ScalarType] { let buffer = UnsafeBufferPointer(start: GetArray(), count: GfMatrix4d.scalarCount) @@ -92,12 +106,24 @@ extension Gf.Matrix4d: Scalar GetOrthonormalized(warn) } - /// Returns an iterator over this scalar. + /// Returns an iterator over this matrix's elements as scalars. /// - /// Since this type can return its own iterator, it conforms - /// this scalar to a sequence to enable many useful operations, - /// like for-in looping and the contains method, without much - /// effort. + /// This allows you to perform scalar operations like iterating + /// through each element with a for-in loop or performing functional + /// transformations. + /// + /// Example: + /// ```swift + /// let matrix: Gf.Matrix4d = ... + /// for scalar in matrix { + /// print(scalar) // Accesses each scalar element of the matrix + /// } + /// ``` + /// + /// The iterator yields the elements in row-major order: + /// Row 0, Column 0 -> Row 0, Column 1 -> ... -> Row 3, Column 3 + /// + /// - Returns: A `ScalarIterator` instance that conforms to `IteratorProtocol`. public func makeIterator() -> ScalarIterator { ScalarIterator(self) @@ -112,11 +138,22 @@ extension Gf.Matrix4d: SIMD public var scalarCount: Int { 16 } + /// Accesses or modifies the element at the specified index in the matrix. + /// + /// This subscript provides direct access to the elements of the matrix as a + /// flat sequence of values. It operates on the underlying storage of the matrix, + /// which is organized as a contiguous array of scalars. The index must be within + /// the range `0.. Self.Scalar { get { - getArray()[index] + data()[index] } set @@ -125,6 +162,84 @@ extension Gf.Matrix4d: SIMD } } + /// Accesses the specified row as a `Gf.Vec4d`. + /// + /// **Data Access**: Row-major order. + /// + /// - Parameter row: The zero-based index of the row to access. + /// + /// - Returns: A 4D vector representing the specified row. + /// - Complexity: O(1) for both `get` and `set`. + /// + /// Example: + /// ```swift + /// let row0 = matrix[0] // Access the first row as a Gf.Vec4d + /// ``` + public subscript(row: Int) -> Gf.Vec4d + { + get + { + getRow(at: row) + } + set + { + setRow(at: row, to: newValue) + } + } + + /// Accesses an individual element of the matrix by its row and column indices. + /// + /// This subscript provides intuitive two-dimensional access to the matrix + /// elements using row and column indices. + /// + /// **Index Mapping**: + /// - Row 0, Column 0 corresponds to the first element + /// - Row 1, Column 2 corresponds to the 6th element + /// + /// - Parameter row: The zero-based index of the row (0...3). + /// - Parameter column: The zero-based index of the column (0...3). + /// + /// - Returns: The scalar value at the specified row and column. + /// + /// Example: + /// ```swift + /// var matrix: Gf.Matrix4d = ... + /// let value = matrix[1, 2] // Access the element at Row 1, Column 2 + /// matrix[2, 0] = 3.0 // Sets the element at Row 2, Column 0 + /// ``` + public subscript(row: Int, column: Int) -> Scalar + { + get + { + // Get the row, then access the column of that row. + getRow(at: row)[column] + } + set + { + // Get the row, modify the column, and then set the updated row. + var rowValues = getRow(at: row) + rowValues[column] = newValue + setRow(at: row, to: rowValues) + } + } + + /// Returns the matrix data as a `SIMD16` vector for optimized computation. + /// + /// The `simd` property combines the 16 scalar components of the 4x4 matrix + /// into a `SIMD16` structure, enabling high-performance operations when using + /// SIMD instructions. + /// + /// **Data Ordering**: Row-major order. + /// ``` + /// // Access the SIMD representation of a Gf.Matrix4d + /// let simdMatrix = matrix.simd + /// ``` + /// + /// Setting this property updates the underlying matrix elements. + /// ```swift + /// var matrix: Gf.Matrix4d = ... + /// matrix.simd = SIMD16(...) // Updates matrix elements in row-major order + /// ``` public var simd: SIMD16 { get @@ -150,25 +265,30 @@ extension Gf.Matrix4d: SIMD } set { - dataMutating()[0] = Scalar(newValue[0]) - dataMutating()[1] = Scalar(newValue[1]) - dataMutating()[2] = Scalar(newValue[2]) - dataMutating()[3] = Scalar(newValue[3]) - dataMutating()[4] = Scalar(newValue[4]) - dataMutating()[5] = Scalar(newValue[5]) - dataMutating()[6] = Scalar(newValue[6]) - dataMutating()[7] = Scalar(newValue[7]) - dataMutating()[8] = Scalar(newValue[8]) - dataMutating()[9] = Scalar(newValue[9]) - dataMutating()[10] = Scalar(newValue[10]) - dataMutating()[11] = Scalar(newValue[11]) - dataMutating()[12] = Scalar(newValue[12]) - dataMutating()[13] = Scalar(newValue[13]) - dataMutating()[14] = Scalar(newValue[14]) - dataMutating()[15] = Scalar(newValue[15]) + for i in 0 ..< 16 + { + dataMutating()[i] = Scalar(newValue[i]) + } } } + /// Initializes a `Gf.Matrix4d` from a SIMD16 vector. + /// + /// The `init` method allows creating a matrix directly from a + /// `SIMD16` vector. The vector's elements are used to populate + /// the matrix in row-major order: + /// - Elements 0-3 -> Row 0 + /// - Elements 4-7 -> Row 1 + /// - Elements 8-11 -> Row 2 + /// - Elements 12-15 -> Row 3 + /// + /// - Parameter simd: A `SIMD16` vector containing the 16 scalar elements. + /// + /// Example: + /// ```swift + /// let simd = SIMD16(0, 1, 2, ..., 15) + /// let matrix = Gf.Matrix4d(simd) + /// ``` public init(_ simd: SIMD16) { self.init() diff --git a/Sources/PixarUSD/Base/Gf/Vec2d.swift b/Sources/PixarUSD/Base/Gf/Vec2d.swift index c74053ac0..b879626d4 100644 --- a/Sources/PixarUSD/Base/Gf/Vec2d.swift +++ b/Sources/PixarUSD/Base/Gf/Vec2d.swift @@ -125,7 +125,7 @@ extension Gf.Vec2d: Scalar /// /// The original length of the vector is returned. /// See also: `Gf.normalize()`. - public mutating func normalize(_ eps: Double) -> Double + public mutating func normalize(eps: Double = GF_MIN_VECTOR_LENGTH) -> Double { Normalize(eps) } @@ -134,7 +134,7 @@ extension Gf.Vec2d: Scalar /// vector after undergoing normalization. If the length of the /// vector is smaller than argument **eps**, then the vector is /// set to the value of `self / eps`. - public func getNormalized(_ eps: Double) -> Self + public func getNormalized(eps: Double = GF_MIN_VECTOR_LENGTH) -> Self { GetNormalized(eps) } diff --git a/Sources/PixarUSD/Base/Gf/Vec2f.swift b/Sources/PixarUSD/Base/Gf/Vec2f.swift index 871bfaab6..4b0d3af06 100644 --- a/Sources/PixarUSD/Base/Gf/Vec2f.swift +++ b/Sources/PixarUSD/Base/Gf/Vec2f.swift @@ -125,7 +125,7 @@ extension Gf.Vec2f: Scalar /// /// The original length of the vector is returned. /// See also: `Gf.normalize()`. - public mutating func normalize(_ eps: Float) -> Float + public mutating func normalize(eps: Float = Float(GF_MIN_VECTOR_LENGTH)) -> Float { Normalize(eps) } @@ -134,7 +134,7 @@ extension Gf.Vec2f: Scalar /// vector after undergoing normalization. If the length of the /// vector is smaller than argument **eps**, then the vector is /// set to the value of `self / eps`. - public func getNormalized(_ eps: Float) -> Self + public func getNormalized(eps: Float = Float(GF_MIN_VECTOR_LENGTH)) -> Self { GetNormalized(eps) } diff --git a/Sources/PixarUSD/Base/Gf/Vec3d.swift b/Sources/PixarUSD/Base/Gf/Vec3d.swift index 5c29ce764..7117bef06 100644 --- a/Sources/PixarUSD/Base/Gf/Vec3d.swift +++ b/Sources/PixarUSD/Base/Gf/Vec3d.swift @@ -128,7 +128,7 @@ extension Gf.Vec3d: Scalar /// /// The original length of the vector is returned. /// See also: `Gf.normalize()`. - public mutating func normalize(_ eps: Double) -> Double + public mutating func normalize(eps: Double = GF_MIN_VECTOR_LENGTH) -> Double { Normalize(eps) } @@ -137,7 +137,7 @@ extension Gf.Vec3d: Scalar /// vector after undergoing normalization. If the length of the /// vector is smaller than argument **eps**, then the vector is /// set to the value of `self / eps`. - public func getNormalized(_ eps: Double) -> Self + public func getNormalized(eps: Double = GF_MIN_VECTOR_LENGTH) -> Self { GetNormalized(eps) } @@ -187,3 +187,123 @@ extension Gf.Vec3d: SIMD self.simd = simd } } + +public extension Gf +{ + /// Returns component-wise multiplication of two vectors. + /// - Parameter v1: A vector to multiply. + /// - Parameter v2: A second vector to multiply with. + /// + /// - Returns: The component-wise multiplication of the two vectors. + @inlinable + static func compMult(_ v1: Gf.Vec3d, _ v2: Gf.Vec3d) -> Gf.Vec3d + { + Pixar.GfCompMult(v1, v2) + } + + /// Returns component-wise division of two vectors. + /// - Parameter v1: A vector to divide. + /// - Parameter v2: A second vector to divide by. + /// + /// - Returns: The component-wise division of the two vectors. + @inlinable + static func compDiv(_ v1: Gf.Vec3d, _ v2: Gf.Vec3d) -> Gf.Vec3d + { + Pixar.GfCompDiv(v1, v2) + } + + /// Returns the dot (inner) product of two vectors. + /// - Parameter v1: A vector to multiply. + /// - Parameter v2: A second vector to multiply with. + /// + /// - Returns: The dot product of the two vectors. + @inlinable + static func dot(_ v1: Gf.Vec3d, _ v2: Gf.Vec3d) -> Double + { + Pixar.GfDot(v1, v2) + } + + /// Returns the geometric length of a vector. + /// - Parameter v: The vector to measure. + /// + /// - Returns: The geometric length of the vector. + @inlinable + static func getLength(_ v: Gf.Vec3d) -> Double + { + Pixar.GfGetLength(v) + } + + /// Normalizes the vector in place to unit length, returning the length before + /// normalization. If the length of the vector is smaller than `eps` then the + /// vector is set to vector/eps, the original length of the vector is returned. + /// - Parameter v: The vector to normalize. + /// - Parameter eps: The length of the vector. + /// + /// - Returns: The length of the vector. + @inlinable + static func normalize(_ v: inout Gf.Vec3d, eps: Double = GF_MIN_VECTOR_LENGTH) -> Double + { + Pixar.GfNormalize(&v, eps) + } + + /// Returns a normalized (unit-length) vector with the same direction as the given + /// vector. If the length of this vector is smaller than `eps`, the vector divided + /// by `eps` is returned. + /// - Parameter v: The vector to normalize. + /// - Parameter eps: The length of the vector. + /// + /// - Returns: The normalized vector. + @inlinable + static func getNormalized(_ v: Gf.Vec3d, eps: Double = GF_MIN_VECTOR_LENGTH) -> Gf.Vec3d + { + Pixar.GfGetNormalized(v, eps) + } + + /// Returns the projection of vector `v1` onto vector `v2`. + /// That is `v2 * (v1 * v2)`. + /// - Parameter v1: The vector to project. + /// - Parameter v2: The vector to project onto. + /// + /// - Returns: The projection of `v1` onto `v2`. + @inlinable + static func getProjection(_ v1: Gf.Vec3d, _ v2: Gf.Vec3d) -> Gf.Vec3d + { + Pixar.GfGetProjection(v1, v2) + } + + /// Returns the orthogonal complement of vector `v1` onto vector `v2`. + /// That is `v1 - v1.getProjection(v2)`. + /// - Parameter v1: The vector to project. + /// - Parameter v2: The vector to project onto. + /// + /// - Returns: The orthogonal complement of `v1` onto `v2`. + @inlinable + static func getComplement(_ v1: Gf.Vec3d, _ v2: Gf.Vec3d) -> Gf.Vec3d + { + Pixar.GfGetComplement(v1, v2) + } + + /// Tests for equality within a given `tolerance`, returning `true` if the + /// length of the difference vector is **less than** or **equal to** `tolerance`. + /// - Parameter v1: The first vector to compare. + /// - Parameter v2: The second vector to compare. + /// - Parameter tolerance: The tolerance to use. + /// + /// - Returns: `true` if the vectors are equal within the tolerance. + @inlinable + static func isClose(_ v1: Gf.Vec3d, _ v2: Gf.Vec3d, within tolerance: Double) -> Bool + { + Pixar.GfIsClose(v1, v2, tolerance) + } + + /// Returns the cross product of two vectors. + /// - Parameter v1: The first vector. + /// - Parameter v2: The second vector. + /// + /// - Returns: The cross product of the two vectors. + @inlinable + static func cross(_ v1: Gf.Vec3d, _ v2: Gf.Vec3d) -> Gf.Vec3d + { + Pixar.GfCross(v1, v2) + } +} diff --git a/Sources/PixarUSD/Base/Gf/Vec3f.swift b/Sources/PixarUSD/Base/Gf/Vec3f.swift index 6f03720d1..198b6bafb 100644 --- a/Sources/PixarUSD/Base/Gf/Vec3f.swift +++ b/Sources/PixarUSD/Base/Gf/Vec3f.swift @@ -128,7 +128,7 @@ extension Gf.Vec3f: Scalar /// /// The original length of the vector is returned. /// See also: `Gf.normalize()`. - public mutating func normalize(_ eps: Float) -> Float + public mutating func normalize(eps: Float = Float(GF_MIN_VECTOR_LENGTH)) -> Float { Normalize(eps) } @@ -137,7 +137,7 @@ extension Gf.Vec3f: Scalar /// vector after undergoing normalization. If the length of the /// vector is smaller than argument **eps**, then the vector is /// set to the value of `self / eps`. - public func getNormalized(_ eps: Float) -> Self + public func getNormalized(eps: Float = Float(GF_MIN_VECTOR_LENGTH)) -> Self { GetNormalized(eps) } diff --git a/Sources/PixarUSD/Base/Gf/Vec4d.swift b/Sources/PixarUSD/Base/Gf/Vec4d.swift index e152a7d52..e3a152a72 100644 --- a/Sources/PixarUSD/Base/Gf/Vec4d.swift +++ b/Sources/PixarUSD/Base/Gf/Vec4d.swift @@ -131,7 +131,7 @@ extension Gf.Vec4d: Scalar /// /// The original length of the vector is returned. /// See also: `Gf.normalize()`. - public mutating func normalize(_ eps: Double) -> Double + public mutating func normalize(eps: Double = GF_MIN_VECTOR_LENGTH) -> Double { Normalize(eps) } @@ -140,7 +140,7 @@ extension Gf.Vec4d: Scalar /// vector after undergoing normalization. If the length of the /// vector is smaller than argument **eps**, then the vector is /// set to the value of `self / eps`. - public func getNormalized(_ eps: Double) -> Self + public func getNormalized(eps: Double = GF_MIN_VECTOR_LENGTH) -> Self { GetNormalized(eps) } diff --git a/Sources/UsdView/Hydra/Hydra+FreeCamera.swift b/Sources/UsdView/Hydra/Hydra+FreeCamera.swift new file mode 100644 index 000000000..8098d9ece --- /dev/null +++ b/Sources/UsdView/Hydra/Hydra+FreeCamera.swift @@ -0,0 +1,132 @@ +/* ---------------------------------------------------------------- + * :: : M E T A V E R S E : :: + * ---------------------------------------------------------------- + * Licensed under the terms set forth in the LICENSE.txt file, this + * file is available at https://openusd.org/license. + * + * Copyright (C) 2016 Pixar. + * Copyright (C) 2024 Wabi Foundation. All Rights Reserved. + * ---------------------------------------------------------------- + * . x x x . o o o . x x x . : : : . o x o . : : : . + * ---------------------------------------------------------------- */ + +import Foundation +import PixarUSD + +class FreeCamera +{ + var position: Pixar.GfVec3d + var front: Pixar.GfVec3d + var up: Pixar.GfVec3d + var right: Pixar.GfVec3d + var worldUp: Pixar.GfVec3d + var yaw: Double + var pitch: Double + var speed: Double + var sensitivity: Double + var zoom: Double + + init(position: Pixar.GfVec3d = Pixar.GfVec3d(0, 0, 10), yaw: Double = -90.0, pitch: Double = 0.0) + { + self.position = position + front = Pixar.GfVec3d(0, 0, -1) + up = Pixar.GfVec3d(0, 1, 0) + right = Pixar.GfVec3d(1, 0, 0) + worldUp = up + self.yaw = yaw + self.pitch = pitch + speed = 2.5 + sensitivity = 0.1 + zoom = 45.0 + updateCameraVectors() + } + + func updateCameraVectors() + { + // calculate the front vector based on yaw and pitch. + let frontX = cos(Gf.degreesToRadians(angle: yaw)) * cos(Gf.degreesToRadians(angle: pitch)) + let frontY = sin(Gf.degreesToRadians(angle: pitch)) + let frontZ = sin(Gf.degreesToRadians(angle: yaw)) * cos(Gf.degreesToRadians(angle: pitch)) + + front = Pixar.GfVec3d(frontX, frontY, frontZ).getNormalized() + + // recalculate right and up vector. + right = Gf.cross(front, worldUp).getNormalized() + up = Gf.cross(right, front).getNormalized() + } + + func processKeyboardInput(forward: Bool, rightward: Bool, upward: Bool) + { + let velocity = speed + + if forward + { + position += front * velocity + } + if rightward + { + position += right * velocity + } + if upward + { + position += up * velocity + } + } + + func processMouseMovement(offsetX: Double, offsetY: Double) + { + yaw += offsetX * sensitivity + pitch -= offsetY * sensitivity + + if pitch > 89.0 + { + pitch = 89.0 + } + if pitch < -89.0 + { + pitch = -89.0 + } + + updateCameraVectors() + } + + func getViewMatrix() -> Gf.Matrix4d + { + // compute the z-axis of the camera (view direction). + let zAxis = (self.position - (self.position + self.front)).getNormalized() + + // compute the x-axis (right vector), the cross product of the up vector and z-axis. + let xAxis = Gf.cross(up, zAxis).getNormalized() + + // compute the y-axis (up vector) as the cross product of z-axis and x-axis. + let yAxis = Gf.cross(zAxis, xAxis) + + // construct the rotation matrix. + var rotation = Gf.Matrix4d(1.0) + rotation[0] = Gf.Vec4d(xAxis[0], yAxis[0], zAxis[0], 0) + rotation[1] = Gf.Vec4d(xAxis[1], yAxis[1], zAxis[1], 0) + rotation[2] = Gf.Vec4d(xAxis[2], yAxis[2], zAxis[2], 0) + rotation[3] = Gf.Vec4d(0, 0, 0, 1) + + rotation.SetTranslate(Gf.Vec3d(-self.position[0], -self.position[1], -self.position[2])) + + return rotation + } + + func getProjectionMatrix(aspectRatio: Double) -> Gf.Matrix4d + { + let fov = Gf.degreesToRadians(angle: self.zoom) + let nearClip = 0.1 + let farClip = 1000.0 + let tanHalfFov = tan(fov / 2.0) + + var matrix = Gf.Matrix4d() + matrix[0][0] = 1.0 / (aspectRatio * tanHalfFov) + matrix[1][1] = 1.0 / tanHalfFov + matrix[2][2] = -(farClip + nearClip) / (farClip - nearClip) + matrix[2][3] = -1.0 + matrix[3][2] = -(2 * farClip * nearClip) / (farClip - nearClip) + + return matrix + } +}