mirror of
https://github.com/GayPizzaSpecifications/voxelotl-engine.git
synced 2025-08-03 05:10:57 +00:00
project mouse into view when clicking w/ mouse unlocked
This commit is contained in:
@ -9,16 +9,18 @@ public final class Camera {
|
||||
static let viewProj = Self(rawValue: 1 << 2)
|
||||
}
|
||||
|
||||
private var _position: SIMD3<Float>
|
||||
private var _rotation: simd_quatf
|
||||
private var _position = SIMD3<Float>.zero
|
||||
private var _rotation = simd_quatf.identity
|
||||
private var _fieldOfView: Float
|
||||
private var _aspectRatio: Float
|
||||
private var _zNearFar: ClosedRange<Float>
|
||||
private var _viewport: Rect<Float>
|
||||
|
||||
private var _dirty: Dirty
|
||||
private var _projection: matrix_float4x4
|
||||
private var _view: matrix_float4x4
|
||||
private var _viewProjection: matrix_float4x4
|
||||
private var _projection = matrix_identity_float4x4
|
||||
private var _view = matrix_identity_float4x4
|
||||
private var _viewProjection = matrix_identity_float4x4
|
||||
private var _invViewProjection = matrix_identity_float4x4
|
||||
|
||||
public var position: SIMD3<Float> {
|
||||
get { self._position }
|
||||
@ -59,8 +61,19 @@ public final class Camera {
|
||||
}
|
||||
}
|
||||
|
||||
public func setSize(_ size: Size<Int>) {
|
||||
self.aspectRatio = Float(size.w) / Float(size.h)
|
||||
public var viewport: Rect<Float> {
|
||||
get { self._viewport }
|
||||
set {
|
||||
self._viewport = newValue
|
||||
self.aspectRatio = Float(newValue.w) / Float(newValue.h)
|
||||
}
|
||||
}
|
||||
public var size: Size<Int> {
|
||||
get { Size<Int>(self._viewport.size) }
|
||||
set {
|
||||
self._viewport.size = Size<Float>(newValue)
|
||||
self.aspectRatio = Float(newValue.w) / Float(newValue.h)
|
||||
}
|
||||
}
|
||||
|
||||
public var range: ClosedRange<Float> {
|
||||
@ -94,20 +107,64 @@ public final class Camera {
|
||||
public var viewProjection: matrix_float4x4 {
|
||||
if !self._dirty.isEmpty {
|
||||
self._viewProjection = self.projection * self.view
|
||||
self._invViewProjection = self._viewProjection.inverse
|
||||
self._dirty.remove(.viewProj)
|
||||
}
|
||||
return self._viewProjection
|
||||
}
|
||||
public var invViewProjection: matrix_float4x4 {
|
||||
if !self._dirty.isEmpty {
|
||||
self._viewProjection = self.projection * self.view
|
||||
self._invViewProjection = self._viewProjection.inverse
|
||||
self._dirty.remove(.viewProj)
|
||||
}
|
||||
return self._invViewProjection
|
||||
}
|
||||
|
||||
init(fov: Float, size: Size<Int>, range: ClosedRange<Float>) {
|
||||
self._position = .zero
|
||||
self._rotation = .identity
|
||||
self._fieldOfView = fov.radians
|
||||
self._aspectRatio = Float(size.w) / Float(size.h)
|
||||
self._zNearFar = range
|
||||
self._viewport = .init(origin: .zero, size: Size<Float>(size))
|
||||
self._dirty = [ .projection, .view, .viewProj ]
|
||||
self._projection = .init()
|
||||
self._view = .init()
|
||||
self._viewProjection = .init()
|
||||
}
|
||||
|
||||
public func screenRay(_ screen: SIMD2<Float>) -> SIMD3<Float> {
|
||||
#if true
|
||||
simd_normalize(self.unproject(screen: SIMD3(screen, 1)) - self.unproject(screen: SIMD3(screen, 0)))
|
||||
#else
|
||||
let inverse = self.projection.inverse
|
||||
var viewportCoord = screen - SIMD2(self._viewport.origin)
|
||||
viewportCoord = (viewportCoord * 2) / SIMD2(self.viewport.size) - 1
|
||||
return simd_normalize(self._rotation * inverse.project(SIMD3(viewportCoord.x, -viewportCoord.y, 1)))
|
||||
#endif
|
||||
}
|
||||
|
||||
public func unproject(screen2D: SIMD2<Float>) -> SIMD3<Float> {
|
||||
self.unproject(screen: SIMD3(screen2D, self._zNearFar.lowerBound))
|
||||
}
|
||||
|
||||
public func unproject(screen: SIMD3<Float>) -> SIMD3<Float> {
|
||||
let inverse = self.invViewProjection
|
||||
|
||||
var viewportCoord = screen.xy - SIMD2(self._viewport.origin)
|
||||
viewportCoord = (viewportCoord * 2) / SIMD2(self._viewport.size) - 1
|
||||
|
||||
#if true
|
||||
return inverse.project(SIMD3(viewportCoord.x, -viewportCoord.y, screen.z))
|
||||
#else
|
||||
let projected = inverse * SIMD4(viewportCoord.x, -viewportCoord.y, screen.z, 1)
|
||||
return projected.xyz * (1 / projected.w)
|
||||
#endif
|
||||
}
|
||||
|
||||
public func project(world position: SIMD3<Float>) -> SIMD2<Float> {
|
||||
let viewport = self.viewProjection * SIMD4(position, 1)
|
||||
if viewport.w == 0 {
|
||||
// World point is exactly on focus point, screenpoint is undefined
|
||||
return .zero
|
||||
}
|
||||
|
||||
return (viewport.xy + 1) * 0.5 * SIMD2(self._viewport.size)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user