Initial
This commit is contained in:
55
README.md
Normal file
55
README.md
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
### Usage Example
|
||||||
|
|
||||||
|
```
|
||||||
|
local playerVob = Vob(getPlayerPtr(heroId))
|
||||||
|
|
||||||
|
Camera.modeChangeEnabled = false
|
||||||
|
Camera.movementEnabled = false
|
||||||
|
|
||||||
|
setTargetVob(playerVob)
|
||||||
|
// or you can specify node
|
||||||
|
setTargetVob(playerVob, "BIP01 HEAD")
|
||||||
|
```
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
```
|
||||||
|
// Public API
|
||||||
|
|
||||||
|
function isContolsDisabled() { return isContolsDisabled }
|
||||||
|
|
||||||
|
function getTargetVob() { return targetVob }
|
||||||
|
function getTargetNode() { return targetNode }
|
||||||
|
|
||||||
|
function getXAxisSensitivity() { return yawSpeed }
|
||||||
|
function getYAxisSensitivity() { return pitchSpeed }
|
||||||
|
function getZoomSensitivity() { return zoomSpeed }
|
||||||
|
|
||||||
|
function isYAxisInverted() { return isPitchInverted }
|
||||||
|
|
||||||
|
function getMinDistance() { return minDistance }
|
||||||
|
function getMaxDistance() { return maxDistance }
|
||||||
|
function getCurrentDistance() { return distance }
|
||||||
|
|
||||||
|
function setTargetVob(vob, node = "") {
|
||||||
|
targetVob = vob
|
||||||
|
targetNode = node
|
||||||
|
|
||||||
|
targetVobMatrix = targetVob.getTrafoModelNodeToWorld(targetNode)
|
||||||
|
currentMatrix = Camera.targetVob.matrix
|
||||||
|
|
||||||
|
currentVisualAlpha = targetVob.visualAlpha
|
||||||
|
maxVisualAlpha = currentVisualAlpha
|
||||||
|
}
|
||||||
|
|
||||||
|
function setControlsDisabled(value) { isControlsDisabled = value }
|
||||||
|
|
||||||
|
function setXAxisSensitivity(value) { yawSpeed = value }
|
||||||
|
function setYAxisSensitivity(value) { pitchSpeed = value }
|
||||||
|
function setZoomSensitivity(value) { zoomSpeed = value }
|
||||||
|
|
||||||
|
function setYAxisInverted(value) { isPitchInverted = value }
|
||||||
|
|
||||||
|
function setMinDistance(value) { minDistance = clamp(value, value, maxDistance) }
|
||||||
|
function setMaxDistance(value) { maxDistance = clamp(value, minDistance, value) }
|
||||||
|
```
|
||||||
242
camera.nut
Normal file
242
camera.nut
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
local DEG_TO_RAD = PI / 180.0
|
||||||
|
|
||||||
|
local distance = 450.0
|
||||||
|
local minDistance = 100.0
|
||||||
|
local maxDistance = 800.0
|
||||||
|
|
||||||
|
local yaw = 0.0
|
||||||
|
local pitch = 1.0
|
||||||
|
|
||||||
|
local targetVob = null
|
||||||
|
local targetNode = ""
|
||||||
|
local targetVobMatrix = null
|
||||||
|
local currentMatrix = null
|
||||||
|
|
||||||
|
local currentVisualAlpha = 1.0
|
||||||
|
local maxVisualAlpha = 1.0
|
||||||
|
|
||||||
|
local yawSpeed = 0.1
|
||||||
|
local pitchSpeed = 0.1
|
||||||
|
local zoomSpeed = 25.0
|
||||||
|
local isPitchInverted = false
|
||||||
|
local isContolsDisabled = false
|
||||||
|
|
||||||
|
// Additional hard coded settings
|
||||||
|
local lerpRatio = 0.3 // Camera rotation smoothness
|
||||||
|
local movementLerpRatio = 0.02 // Camera movement smoothness
|
||||||
|
local collisionOffset = 50.0 // Distance offset from collision objects
|
||||||
|
local distanceThreshold = 100.0 // Distance below that value will apply vob transparency
|
||||||
|
local pitchThreshold = 150.0 // Horizontal rotation below this angle will apply vob transparency
|
||||||
|
|
||||||
|
// Utility
|
||||||
|
|
||||||
|
local function mat4Lerp(mat1, mat2, lerpRatio) {
|
||||||
|
local transposedMat1 = mat1.transpose()
|
||||||
|
local transposedMat2 = mat2.transpose()
|
||||||
|
|
||||||
|
local out = []
|
||||||
|
|
||||||
|
for (local i = 0; i < 4; i++)
|
||||||
|
out.append(Vec4.lerp(
|
||||||
|
lerpRatio,
|
||||||
|
transposedMat1[i],
|
||||||
|
transposedMat2[i]
|
||||||
|
))
|
||||||
|
|
||||||
|
local outMat = Mat4(out[0], out[1], out[2], out[3]).transpose()
|
||||||
|
outMat.makeOrthonormal()
|
||||||
|
return outMat
|
||||||
|
}
|
||||||
|
|
||||||
|
local function interpolateArc(pos1, pos2, center, t) {
|
||||||
|
local vec1 = pos1 - center
|
||||||
|
local vec2 = pos2 - center
|
||||||
|
|
||||||
|
local len1 = vec1.len()
|
||||||
|
local len2 = vec2.len()
|
||||||
|
local len = lerp(len1, len2, t)
|
||||||
|
|
||||||
|
vec1 = vec1.normalizeSafe()
|
||||||
|
vec2 = vec2.normalizeSafe()
|
||||||
|
|
||||||
|
local dot = Vec3.dot(vec1, vec2)
|
||||||
|
local angle = acos(clamp(dot, -1.0, 1.0))
|
||||||
|
|
||||||
|
if (angle < 0.001) {
|
||||||
|
local resultDir = Vec3.lerp(t, vec1, vec2)
|
||||||
|
return center + resultDir * len
|
||||||
|
}
|
||||||
|
|
||||||
|
local t1 = sin((1.0 - t) * angle)
|
||||||
|
local t2 = sin(t * angle)
|
||||||
|
|
||||||
|
local resultDir = vec1 * t1 + vec2 * t2
|
||||||
|
resultDir = resultDir.normalizeSafe()
|
||||||
|
|
||||||
|
return center + resultDir * len
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private API
|
||||||
|
|
||||||
|
local function interpolateCameraMatrix(targetMatrix, center) {
|
||||||
|
local currentPosition = currentMatrix.getTranslation()
|
||||||
|
local targetPosition = targetMatrix.getTranslation()
|
||||||
|
|
||||||
|
local currentRotation = Quat()
|
||||||
|
local targetRotation = Quat()
|
||||||
|
currentRotation.fromMat4(currentMatrix)
|
||||||
|
targetRotation.fromMat4(targetMatrix)
|
||||||
|
|
||||||
|
local position = interpolateArc(
|
||||||
|
currentPosition,
|
||||||
|
targetPosition,
|
||||||
|
center,
|
||||||
|
lerpRatio
|
||||||
|
)
|
||||||
|
|
||||||
|
local rotation = Quat.slerp(
|
||||||
|
lerpRatio,
|
||||||
|
currentRotation,
|
||||||
|
targetRotation
|
||||||
|
)
|
||||||
|
|
||||||
|
local forward = (center - position).normalize()
|
||||||
|
local rotationMat = rotation.toMat4()
|
||||||
|
local up = rotationMat.getUpVector()
|
||||||
|
|
||||||
|
return Mat4.lookAt(position, center, up)
|
||||||
|
}
|
||||||
|
|
||||||
|
local function getSafeDistance(vobPosition, cameraPosition) {
|
||||||
|
local direction = cameraPosition - vobPosition
|
||||||
|
|
||||||
|
local report = GameWorld.traceRayNearestHit(
|
||||||
|
vobPosition,
|
||||||
|
direction,
|
||||||
|
TRACERAY_POLY_NORMAL | TRACERAY_VOB_IGNORE_CHARACTER | TRACERAY_POLY_IGNORE_TRANSP
|
||||||
|
)
|
||||||
|
if (report == null) return distance
|
||||||
|
|
||||||
|
return vobPosition.distance(report.intersect) - collisionOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
local function updateCameraMatrix() {
|
||||||
|
local currentVobMatrix = targetVob.getTrafoModelNodeToWorld(targetNode)
|
||||||
|
targetVobMatrix = mat4Lerp(targetVobMatrix, currentVobMatrix, movementLerpRatio)
|
||||||
|
local targetVobPosition = targetVobMatrix.getTranslation()
|
||||||
|
|
||||||
|
local yawRad = yaw * DEG_TO_RAD
|
||||||
|
local pitchRad = pitch * DEG_TO_RAD
|
||||||
|
|
||||||
|
local verticalFactor = cos(pitchRad)
|
||||||
|
local horizontalFactor = sin(pitchRad)
|
||||||
|
|
||||||
|
local targetCameraPosition = Vec3(
|
||||||
|
targetVobPosition.x + sin(yawRad) * distance * horizontalFactor,
|
||||||
|
targetVobPosition.y + distance * verticalFactor,
|
||||||
|
targetVobPosition.z + cos(yawRad) * distance * horizontalFactor
|
||||||
|
)
|
||||||
|
|
||||||
|
local targetMatrix = Mat4.lookAt(
|
||||||
|
targetCameraPosition,
|
||||||
|
targetVobPosition,
|
||||||
|
Vec3(0, 1, 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
local safeDistance = getSafeDistance(targetVobPosition, targetCameraPosition)
|
||||||
|
if (safeDistance != distance) {
|
||||||
|
local minSafeDistance = 10.0
|
||||||
|
safeDistance = clamp(safeDistance, minSafeDistance, maxDistance)
|
||||||
|
targetCameraPosition = Vec3(
|
||||||
|
targetVobPosition.x + sin(yawRad) * safeDistance * horizontalFactor,
|
||||||
|
targetVobPosition.y + safeDistance * verticalFactor,
|
||||||
|
targetVobPosition.z + cos(yawRad) * safeDistance * horizontalFactor
|
||||||
|
)
|
||||||
|
targetMatrix.setTranslation(targetCameraPosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
currentMatrix = interpolateCameraMatrix(targetMatrix, targetVobPosition)
|
||||||
|
Camera.targetVob.matrix = currentMatrix
|
||||||
|
}
|
||||||
|
|
||||||
|
local function updateAlpha() {
|
||||||
|
local targetRatio = maxVisualAlpha
|
||||||
|
|
||||||
|
local cameraPosition = currentMatrix.getTranslation()
|
||||||
|
local targetPosition = targetVobMatrix.getTranslation()
|
||||||
|
local currentDistance = cameraPosition.distance(targetPosition)
|
||||||
|
|
||||||
|
if (currentDistance < distanceThreshold)
|
||||||
|
targetRatio = (currentDistance / distanceThreshold) * (maxVisualAlpha / 2.0)
|
||||||
|
else if (pitch > pitchThreshold)
|
||||||
|
targetRatio = ((179.0 - pitch) / 29.0) * (maxVisualAlpha / 2.0)
|
||||||
|
|
||||||
|
targetRatio = clamp(targetRatio, 0.0, 1.0)
|
||||||
|
currentVisualAlpha = lerp(currentVisualAlpha, targetRatio, 0.1)
|
||||||
|
targetVob.visualAlpha = currentVisualAlpha
|
||||||
|
}
|
||||||
|
|
||||||
|
addEventHandler("onMouseMove", function(x, y) {
|
||||||
|
if (isContolsDisabled) return
|
||||||
|
|
||||||
|
local pitchMultiplier = isPitchInverted ? -1 : 1
|
||||||
|
|
||||||
|
yaw += x * yawSpeed
|
||||||
|
pitch += y * pitchSpeed * pitchMultiplier
|
||||||
|
|
||||||
|
yaw = (yaw % 360 + 360) % 360
|
||||||
|
pitch = clamp(pitch, 1.0, 179.0)
|
||||||
|
})
|
||||||
|
|
||||||
|
addEventHandler("onMouseWheel", function(direction) {
|
||||||
|
if (isContolsDisabled) return
|
||||||
|
|
||||||
|
distance -= direction * zoomSpeed
|
||||||
|
distance = clamp(distance, minDistance, maxDistance)
|
||||||
|
})
|
||||||
|
|
||||||
|
addEventHandler("onRender", function() {
|
||||||
|
if (targetVob == null) return
|
||||||
|
|
||||||
|
updateCameraMatrix()
|
||||||
|
updateAlpha()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Public API
|
||||||
|
|
||||||
|
function isContolsDisabled() { return isContolsDisabled }
|
||||||
|
|
||||||
|
function getTargetVob() { return targetVob }
|
||||||
|
function getTargetNode() { return targetNode }
|
||||||
|
|
||||||
|
function getXAxisSensitivity() { return yawSpeed }
|
||||||
|
function getYAxisSensitivity() { return pitchSpeed }
|
||||||
|
function getZoomSensitivity() { return zoomSpeed }
|
||||||
|
|
||||||
|
function isYAxisInverted() { return isPitchInverted }
|
||||||
|
|
||||||
|
function getMinDistance() { return minDistance }
|
||||||
|
function getMaxDistance() { return maxDistance }
|
||||||
|
function getCurrentDistance() { return distance }
|
||||||
|
|
||||||
|
function setTargetVob(vob, node = "") {
|
||||||
|
targetVob = vob
|
||||||
|
targetNode = node
|
||||||
|
|
||||||
|
targetVobMatrix = targetVob.getTrafoModelNodeToWorld(targetNode)
|
||||||
|
currentMatrix = Camera.targetVob.matrix
|
||||||
|
|
||||||
|
currentVisualAlpha = targetVob.visualAlpha
|
||||||
|
maxVisualAlpha = currentVisualAlpha
|
||||||
|
}
|
||||||
|
|
||||||
|
function setControlsDisabled(value) { isControlsDisabled = value }
|
||||||
|
|
||||||
|
function setXAxisSensitivity(value) { yawSpeed = value }
|
||||||
|
function setYAxisSensitivity(value) { pitchSpeed = value }
|
||||||
|
function setZoomSensitivity(value) { zoomSpeed = value }
|
||||||
|
|
||||||
|
function setYAxisInverted(value) { isPitchInverted = value }
|
||||||
|
|
||||||
|
function setMinDistance(value) { minDistance = clamp(value, value, maxDistance) }
|
||||||
|
function setMaxDistance(value) { maxDistance = clamp(value, minDistance, value) }
|
||||||
Reference in New Issue
Block a user