Skip to content

Commit

Permalink
Support bones (#15)
Browse files Browse the repository at this point in the history
* Support bones

* Improve rotation animation
  • Loading branch information
boatbomber authored Jun 18, 2023
1 parent 1ee782e commit 5f115d1
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 38 deletions.
2 changes: 1 addition & 1 deletion src/Settings.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ local SettingTypes = {
PivotOffset = "CFrame",
}

function Settings.new(object: Instance, base)
function Settings.new(object: BasePart | Bone, base)
local inst = {}

-- Initial settings
Expand Down
48 changes: 34 additions & 14 deletions src/VectorMap.lua
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ function VectorMap:_debugDrawVoxel(voxelKey: Vector3)
end

function VectorMap:AddObject(position: Vector3, object: any)
local className = object.ClassName

local voxelSize = self._voxelSize
local voxelKey = Vector3.new(
math.floor(position.X / voxelSize),
Expand All @@ -38,23 +40,32 @@ function VectorMap:AddObject(position: Vector3, object: any)

local voxel = self._voxels[voxelKey]

if not voxel then
self._voxels[voxelKey] = { object }
return voxelKey
if voxel == nil then
self._voxels[voxelKey] = {
[className] = { object },
}
elseif voxel[className] == nil then
voxel[className] = { object }
else
table.insert(voxel[className], object)
end

table.insert(voxel, object)
return voxelKey
end

function VectorMap:RemoveObject(voxelKey: Vector3, object: any)
local voxel = self._voxels[voxelKey]

if not voxel then
if voxel == nil then
return
end

local className = object.ClassName
if voxel[className] == nil then
return
end

for index, storedObject in voxel do
for index, storedObject in voxel[className] do
if storedObject == object then
-- Swap remove to avoid shifting
local n = #voxel
Expand All @@ -64,17 +75,22 @@ function VectorMap:RemoveObject(voxelKey: Vector3, object: any)
end
end

if #voxel == 0 then
-- Remove empty class group
if #voxel[className] == 0 then
voxel[className] = nil

-- Remove empty voxel
self._voxels[voxelKey] = nil
if next(voxel) == nil then
self._voxels[voxelKey] = nil
end
end
end

function VectorMap:GetVoxel(voxelKey: Vector3)
return self._voxels[voxelKey]
end

function VectorMap:ForEachObjectInRegion(top: Vector3, bottom: Vector3, callback: (any) -> ())
function VectorMap:ForEachObjectInRegion(top: Vector3, bottom: Vector3, callback: (string, any) -> ())
local voxelSize = self._voxelSize
local xMin, yMin, zMin = math.min(bottom.X, top.X), math.min(bottom.Y, top.Y), math.min(bottom.Z, top.Z)
local xMax, yMax, zMax = math.max(bottom.X, top.X), math.max(bottom.Y, top.Y), math.max(bottom.Z, top.Z)
Expand All @@ -87,15 +103,17 @@ function VectorMap:ForEachObjectInRegion(top: Vector3, bottom: Vector3, callback
continue
end

for _, object in voxel do
callback(object)
for className, objects in voxel do
for _, object in objects do
callback(className, object)
end
end
end
end
end
end

function VectorMap:ForEachObjectInView(camera: Camera, distance: number, callback: (any) -> ())
function VectorMap:ForEachObjectInView(camera: Camera, distance: number, callback: (string, any) -> ())
local voxelSize = self._voxelSize
local cameraCFrame = camera.CFrame
local cameraPos = cameraCFrame.Position
Expand Down Expand Up @@ -201,8 +219,10 @@ function VectorMap:ForEachObjectInView(camera: Camera, distance: number, callbac
for fillZ = entry, exit do
local voxel = self._voxels[Vector3.new(x, y, fillZ)]
if voxel then
for _, object in voxel do
callback(object)
for className, objects in voxel do
for _, object in objects do
callback(className, object)
end
end
end
end
Expand Down
78 changes: 55 additions & 23 deletions src/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,12 @@ function WindShake:Connect(funcName: string, event: RBXScriptSignal): RBXScriptC
end)
end

function WindShake:AddObjectShake(object: BasePart, settingsTable: WindShakeSettings?)
function WindShake:AddObjectShake(object: BasePart | Bone, settingsTable: WindShakeSettings?)
if typeof(object) ~= "Instance" then
return
end

if not object:IsA("BasePart") then
if not (object:IsA("BasePart") or object:IsA("Bone")) then
return
end

Expand All @@ -85,11 +85,14 @@ function WindShake:AddObjectShake(object: BasePart, settingsTable: WindShakeSett
end

metadata[object] = {
ChunkKey = self.VectorMap:AddObject(object.Position, object),
ChunkKey = self.VectorMap:AddObject(
if object:IsA("Bone") then object.WorldPosition else object.Position,
object
),
Settings = Settings.new(object, DEFAULT_SETTINGS),

Seed = math.random(5000) * 0.32,
Origin = object.CFrame,
Origin = if object:IsA("Bone") then object.WorldCFrame else object.CFrame,
LastUpdate = os.clock(),
}

Expand All @@ -100,7 +103,7 @@ function WindShake:AddObjectShake(object: BasePart, settingsTable: WindShakeSett
ObjectShakeAddedEvent:Fire(object)
end

function WindShake:RemoveObjectShake(object: BasePart)
function WindShake:RemoveObjectShake(object: BasePart | Bone)
if typeof(object) ~= "Instance" then
return
end
Expand All @@ -116,6 +119,8 @@ function WindShake:RemoveObjectShake(object: BasePart)

if object:IsA("BasePart") then
object.CFrame = objMeta.Origin
elseif object:IsA("Bone") then
object.WorldCFrame = objMeta.Origin
end
end

Expand All @@ -125,14 +130,16 @@ end
function WindShake:Update(deltaTime: number)
debug.profilebegin("WindShake")

local active = 0

debug.profilebegin("Update")

local now = os.clock()
local slowerDeltaTime = deltaTime * 3
local step = math.min(1, deltaTime * 5)

-- Reuse tables to avoid garbage collection
local i = 0
local bulkMoveIndex = 0
local partList = self._partList
local cframeList = self._cframeList
table.clear(partList)
Expand All @@ -146,12 +153,13 @@ function WindShake:Update(deltaTime: number)
local maxRefreshRate = self.MaxRefreshRate

-- Update objects in view at their respective refresh rates
self.VectorMap:ForEachObjectInView(camera, renderDistance, function(object)
self.VectorMap:ForEachObjectInView(camera, renderDistance, function(className: string, object: BasePart | Bone)
local objMeta = objectMetadata[object]
local lastUpdate = objMeta.LastUpdate or 0
local isBone = className == "Bone"

-- Determine if the object refresh rate
local objectCFrame = object.CFrame
local objectCFrame = if isBone then (object :: Bone).WorldCFrame else object.CFrame
local distanceAlpha = ((cameraPos - objectCFrame.Position).Magnitude / renderDistance)
local distanceAlphaSq = distanceAlpha * distanceAlpha
local jitter = (1 / math.random(60, 120))
Expand All @@ -163,29 +171,53 @@ function WindShake:Update(deltaTime: number)
end

objMeta.LastUpdate = now
active += 1

local objSettings = objMeta.Settings
local seed = objMeta.Seed
local amp = objSettings.WindPower * 0.1
local amp = objSettings.WindPower * 0.2
local lowAmp = amp / 3
local freq = now * (objSettings.WindSpeed * 0.08)

i += 1
partList[i] = object
cframeList[i] = objectCFrame:Lerp(
(
(objMeta.Origin * objSettings.PivotOffset)
local animValue = (math.noise(freq, 0, seed) + 0.4) * amp
local lerpAlpha = math.clamp(step + distanceAlphaSq, 0.1, 0.5)

local origin = objMeta.Origin * objSettings.PivotOffset
local windDirection = objSettings.WindDirection.Unit
local localWindDirection = origin:VectorToObjectSpace(windDirection)

if isBone then
local bone: Bone = object :: Bone
bone.Transform = bone.Transform:Lerp(
(
CFrame.fromAxisAngle(localWindDirection:Cross(Vector3.yAxis), -animValue)
* CFrame.Angles(
math.noise(freq, 0, seed) * amp,
math.noise(freq, 0, -seed) * amp,
math.noise(freq, 0, seed + seed) * amp
math.noise(seed, 0, freq) * lowAmp,
math.noise(seed, freq, 0) * lowAmp,
math.noise(freq, seed, 0) * lowAmp
)
+ objSettings.WindDirection * ((0.5 + math.noise(freq, seed, seed)) * amp)
) * objSettings.PivotOffsetInverse,
math.clamp(step + distanceAlphaSq, 0.1, 0.9)
)
) + (localWindDirection * animValue * amp),
lerpAlpha
)
else
bulkMoveIndex += 1
partList[bulkMoveIndex] = object
cframeList[bulkMoveIndex] = objectCFrame:Lerp(
(
origin
* CFrame.fromAxisAngle(localWindDirection:Cross(Vector3.yAxis), -animValue)
* CFrame.Angles(
math.noise(seed, 0, freq) * lowAmp,
math.noise(seed, freq, 0) * lowAmp,
math.noise(freq, seed, 0) * lowAmp
)
* objSettings.PivotOffsetInverse
) + (windDirection * animValue * (amp * 2)),
lerpAlpha
)
end
end)

self.Active = i
self.Active = active

debug.profileend()

Expand Down

0 comments on commit 5f115d1

Please sign in to comment.