its the same equation and don’t use the cframe use the position
What do you mean you’re solving for the entire position?
https://gyazo.com/e84c56f898332d8240a6480f10298c29
I’m still getting these results while using a bodyPos with the X being the position’s X.
function sin(a, b, c, d, e, f)
local x = a * math.sin(b * c + (d * e)) + f
return x
end
if root ~= nil and root:IsDescendantOf(player.Character) then
bp.Position = Vector3.new(0, sin(amplitude, root.Position.X, frequency, syncedTime + increment, speed, height), 0)
bp.Parent = root
root.Parent.Humanoid.WalkSpeed = 32
end
Its because they are trying to help people who aren’t as talented as you at converting ideas to scripts.
How’d you make the server to client syncronizer, can’t seem to get to understand it.
Thank you so much for this quality post! I already got a skinned mesh sea working, but I was really struggling with implementing Gerstner waves.
Thanks, this really helped me out with understanding skinned meshes! I have 0 experience with Blender, so I was quite glad to have found this post.
What an amazing feat! Well done, I will use this for future projects.
Is there any way to randomize the waves? I have tried using math.noise and math.random but they both break the mesh.
Sorry for a little bump, but with like 1 small correction I got this:
local plane = workspace:WaitForChild("Ocean"):WaitForChild("Plane")
local origPosTable = {}
local LocalPlayer = game.Players.LocalPlayer
local speedCounter = 20
local maxDistance = 10
for _, bone in pairs(plane:GetChildren()) do
if bone:IsA("Bone") then
table.insert(origPosTable,bone.Position)
origPosTable[bone] = bone.Position
end
end
--GerstnerWave(Vector3 SamplePosition, Float Wavelength, Vector2 Direction, Float Steepness, Float Gravity, Float SampleTick)
function GerstnerWave(SamplePosition,Wavelength,Direction,Steepness,Gravity,SampleTick)
local k = (2 * math.pi) / Wavelength
local a = Steepness/k
local d = Direction.Unit
local c = math.sqrt(Gravity / k)
local f = k * d:Dot(Vector2.new(SamplePosition.X,SamplePosition.Z)) - c * SampleTick
local cosF = math.cos(f)
--Displacement Vectors
local dX = (d.X * (a * cosF))
local dY = a * math.sin(f)
local dZ = ( d.Y * (a * cosF))
return Vector3.new(dX,dY,dZ)
end
game:GetService("RunService").Heartbeat:Connect(function()
for _, bone in pairs(plane:GetChildren()) do
if bone:IsA("Bone") then
if origPosTable[bone] then
local SamplePosition = bone.WorldPosition
local Wave1 = GerstnerWave(SamplePosition, 150, Vector2.new(1, 0), .05, 1, speedCounter)
local Wave2 = GerstnerWave(SamplePosition, 175, Vector2.new(0, .3), .2, 1.25, speedCounter)
local Wave3 = GerstnerWave(SamplePosition, 200, Vector2.new(1, 1), .1, 1.5, speedCounter)
local TotalDisplacement = Wave1 + Wave2 + Wave3
bone.Position = origPosTable[bone] + TotalDisplacement
end
end
end
end)
localscript in startergui, it works but it freezes, so it only goes for like a millisecond and then it just stops without any errors nor output
Update: Got this working
Now I need some way to make objects and characters interact with the waves, so they ¨collide¨
You are correct! i did ‘stumble’ upon your post; it is timely, excellent information; as i am just learning blender this provides yet another project.
Thank You!
To be honest, I’d avoid body movers if you intend to keep original character functionality. When you start messing with BMs then you lose some core mechanics. What I do instead is create a small platform underneath the player that positions itself underneath the character, set the plane to not collide with the character then calculate the GerstnerWave, get the Y and adjust the platform to the Y. The result is the player remains on the platform due to the Hipheight popping the character up while maintaining its state.
https://gyazo.com/f10f1bcd1e51f965f561aef0ef7fb81a
Using the Ocean.rbxl provided in the post, forking it and adding additional code at the bottom, we effectively have a platform that simulates staying afloat.
-- Setup wave movement connection
local skipCounter = 0
local speedCounter = 0
game:GetService("RunService").Heartbeat:Connect(function()
skipCounter += 1
speedCounter += speedMultiple
if skipCounter >= 2 then
skipCounter = 0
local char = LocalPlayer.Character
if char and char:FindFirstChild("HumanoidRootPart") then
for _, bone in pairs(plane:GetChildren()) do
if bone:IsA("Bone") then
if origPosTable[bone] then
if (char.HumanoidRootPart.Position - bone.WorldPosition).Magnitude <= maxDistance then
local SamplePosition = bone.WorldPosition
local Wave1 = GerstnerWave(SamplePosition, 150, Vector2.new(1, 0), .05, 1, speedCounter)
local Wave2 = GerstnerWave(SamplePosition, 175, Vector2.new(0, .3), .2, 1.25, speedCounter)
local Wave3 = GerstnerWave(SamplePosition, 200, Vector2.new(1, 1), .1, 1.5, speedCounter)
local TotalDisplacement = Wave1 + Wave2 + Wave3
bone.Position = origPosTable[bone] + TotalDisplacement
end;
end;
end;
end;
workspace.EEERRR.Position = LocalPlayer.Character:GetPrimaryPartCFrame()
* CFrame.new(0, -LocalPlayer.Character.Humanoid.HipHeight/2, 0).Position;
local BoxSamplePosition = workspace.EEERRR.Position;
local Wave1 = GerstnerWave(BoxSamplePosition, 150, Vector2.new(1, 0), .05, 1, speedCounter)
local Wave2 = GerstnerWave(BoxSamplePosition, 175, Vector2.new(0, .3), .2, 1.25, speedCounter)
local Wave3 = GerstnerWave(BoxSamplePosition, 200, Vector2.new(1, 1), .1, 1.5, speedCounter)
local TotalDisplacement = Wave1 + Wave2 + Wave3
workspace.EEERRR.Position = Vector3.new(
BoxSamplePosition.X,
TotalDisplacement.Y + workspace.EEERRR.Size.Y/2,
BoxSamplePosition.Z
);
end;
end
end)
Of course, you’ll want to clean it up, however, and make it work for you. I haven’t tested it going from land to the deformation water. Guess you can do some raycasting to see if you’re hitting the plane or not and have the platform gradually push you up to water level? (I did this in like 7 minutes so happy problem solving)
Hey! Thanks for this, but when I playtest the whole plane actually moves away from where its supposed to be. What could be the reason and how am I supposed to fix this? Thanks. Here’s my code:
local origPosTable = {}
for i, bone in pairs(workspace.Ocean.Plane:GetChildren()) do
if bone:IsA("Bone") then
origPosTable[bone] = bone.Position
end
end
function GerstnerWave(SamplePosition,Wavelength,Direction,Steepness,Gravity,SampleTick)
local k = (2 * math.pi) / Wavelength
local a = Steepness/k
local d = Direction.Unit
local c = math.sqrt(Gravity / k)
local f = k * d:Dot(Vector2.new(SamplePosition.X,SamplePosition.Z)) - c * SampleTick
local cosF = math.cos(f)
--Displacement Vectors
local dX = (d.X * (a * cosF))
local dY = a * math.sin(f)
local dZ = ( d.Y * (a * cosF))
return Vector3.new(dX,dY,dZ)
end
game:GetService("RunService").Heartbeat:Connect(function()
for i, bone in pairs(workspace.Ocean.Plane:GetChildren()) do
if bone:IsA("Bone") then
local bonePos = GerstnerWave(origPosTable[bone], 90, Vector2.new(1, 0), .2, 1.5, tick())
bone.WorldPosition = origPosTable[bone] + bonePos
end
end
end)
Oh nvm, I had to set bone.Position and not bone.WorldPosition. For anybody stumbling upon this in the future make sure you’re setting the bone’s position and not world position! Have a good day!
Prety well written for a first tutorial… Thanks for this! I have been trying to figure out how to import such meshes into Roblox to no avail, and the formula is pretty decent if you ask me. I am playing with some tweaks to make it look a tad smoother but overall, I am so happy to have found your post!
Thanks!
Enabling use_normals
causes horrible results for me (I’m trying to subdivide a cylinder):
Any idea what might be going on?
EDIT: Was able to fix it. Code I used below:
objects = bpy.context.view_layer.objects
obj = objects.active
def AddBonesAtVertices(obj, length, use_normals):
if not obj or obj.type != 'MESH':
return
points = []
normals = []
data = []
for v in obj.data.vertices:
p = obj.matrix_world @ v.co
dir = p * Vector((1, 0, 1))
dir.normalize()
n = p + dir * length
points.append(p)
if not use_normals:
n = Vector((p[0], p[1], p[2] + length))
normals.append(n)
data.append([p, n])
bpy.ops.object.mode_set(mode='OBJECT')
amt = bpy.data.armatures.new(obj.name + "_vBones")
rig = bpy.data.objects.new(obj.name + '_vRig', amt)
bpy.context.collection.objects.link(rig)
objects.active = rig
names = [] #Will keep bone names
bpy.ops.object.editmode_toggle()
for i, l in enumerate(zip(points, normals)):
bone = amt.edit_bones.new(str(i))
bone.head = l[0]
bone.tail = l[1]
names.append(bone.name) #Add name
bpy.ops.object.editmode_toggle()
for v_index, name in enumerate(names):
#Get the group
group = obj.vertex_groups.new(name=name)
#Link the vertex to it
group.add([v_index], 1, 'REPLACE')
#Parent and add modifier
obj.parent = rig
modifier = obj.modifiers.new(rig.name, "ARMATURE")
modifier.object = rig
AddBonesAtVertices(obj, 0.5, True)
The result:
Is there a way to prevent the mesh from clipping through objects as an example boats.
Is there anyway to make the tile smaller? I set the size to be 500x500, but it stills looks 2000x2000? I needa be able to tile it together, but have gaps for underground areas to work
Sorry for huge bump, but it’s not working as intended
for _, bone in plane:GetChildren() do
if not bone:IsA("Bone") then
continue
end
if not self.OriginalPositions[bone] then
continue
end
if Player:DistanceFromCharacter(bone.WorldPosition) > MAX_DISTANCE then
continue
end
local SamplePosition = bone.WorldPosition
local Wave1 = GerstnerWave(SamplePosition, 150, Vector2.new(1, 0), 0.025, 1, self.SpeedCounter[plane])
local Wave2 = GerstnerWave(SamplePosition, 175, Vector2.new(0, 0.3), 0.08, 1.25, self.SpeedCounter[plane])
local Wave3 = GerstnerWave(SamplePosition, 200, Vector2.new(1, 1), 0.04, 1.5, self.SpeedCounter[plane])
local TotalDisplacement = Wave1 + Wave2 + Wave3
bone.Position = self.OriginalPositions[bone] + TotalDisplacement
end
local BoxSamplePosition = Character:GetPivot().Position - Vector3.new(0, Character.Humanoid.HipHeight / 2, 0)
local Wave1 = GerstnerWave(BoxSamplePosition, 150, Vector2.new(1, 0), 0.025, 1, self.SpeedCounter[plane])
local Wave2 = GerstnerWave(BoxSamplePosition, 175, Vector2.new(0, 0.3), 0.08, 1.25, self.SpeedCounter[plane])
local Wave3 = GerstnerWave(BoxSamplePosition, 200, Vector2.new(1, 1), 0.04, 1.5, self.SpeedCounter[plane])
local TotalDisplacement = Wave1 + Wave2 + Wave3
workspace.EEERRR.Position =
Vector3.new(BoxSamplePosition.X, 13 + TotalDisplacement.Y - workspace.EEERRR.Size.Y / 2, BoxSamplePosition.Z)
Sorry for bump, but can you give more detail on how to do this? (Sorry I am new to blender)
The script gives error when ran (note: I am using blender 2.79 because new version isn’t supported on my pc)