Need help with aligning orientation on mesh deformation ocean

Hey everybody,
So I believe the title says it all. So what I want to do is that on my mesh deformation ocean I want to align the parts to the waves. I got position aligned successfully, but I have no clue on how to align the orientation.
https://streamable.com/hqpcqt

Here’s the current gerstner wave function am using:

local 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

Then I just add the displacement vector the function returns to the original position of the bone which I cache in a table at the start of the script.

for i, bone in pairs(workspace.Ocean.Plane:GetChildren()) do
	if bone:IsA("Bone") then
		origPosTable[bone] = bone.Position
	end
end
game:GetService("RunService").Heartbeat:Connect(function()
	speedCounter += script:GetAttribute("WaveSpeed")
	-- simulate the "waves"
	for i, bone in pairs(workspace.Ocean.Plane:GetChildren()) do
		if bone:IsA("Bone") then
			local displacement = GerstnerWave(bone.WorldPosition, script:GetAttribute("WaveLength"), script:GetAttribute("Direction"), script:GetAttribute("Steepness"), script:GetAttribute("Gravity"), speedCounter)			
			bone.Position = origPosTable[bone] + displacement
		end
	end
end)

What I tried:

  1. Using attachments, positioning them according to the wave at their point and then calculating the angle between them
  2. Add the displacement returned to the orientation too (I knew it wouldn’t work but was just testing)

But nothing worked.
While replying please give me some resources and not the full script, I want to learn new things, you can also tell me what functions/things I would have to use for this, but please keep in mind am just in 8th grade so I dont know much about these things.

Going to sleep now, will reply tomorrow

1 Like

Ideally you should use physics to simulate buoyancy.

But if you want to rigidly calculate the surface of the water you can use three points (three sample positions) to form a plane.

image

This plane will have a normal vector which you can use to align the part up vector with the generated normal vector, thus “aligning orientation” to the ocean

Similar to this method:

5 Likes

Raycasting towards the water will return the point of impact as well as the surface normal. The surface normal points perpendicular to the surface of the wave.
Here’s one guy’s take on it and a slight fix I gave him.

1 Like

When you move the bones of a deformed part it wont respond to raycasts and If you raycast down it’ll hit the original position of the ocean, so it assumes that the ocean is flat and hence results are not good. I’ve tried this before too.

I literally saw that video yesterday :joy: but unfortunately I could understand only half of the things since it was all in c++. I’ll try to give it another look today.

I’ve thought of a way to add 4 invisible parts in the main part (which will be named “bow”, “stern”, “port”, “starboard”. bow is placed at the front tip, stern at the rear tip and port and starboard at left and right tips), calculate the water height at each part and then get the angle, then set it in the orientation. May/may not work. I think you mean something similar to this?

Here’s what I’m using for angle calculation:

local function angleBetweenVectors(a: Vector3, b: Vector3)
    return math.acos(a:Dot(b) / (a.Magnitude * b.Magnitude))
end

That uses raycasting, it won’t work.

Once you move the mesh bones, the physics engine still takes into account the original position of the ocean, which is flat, so if you drop a part it will not stay above the waves but it’d rather fall down and hit the original “flat” ocean. If you know what I mean. Raycasts and collisions will always react to the flat original position and hence you need to calculate everything rigidly.

Thanks for elaborating nicely :grin:

You don’t need to raycast the resource I showed you just shows how to calculate the up vector using three points.

I mean you already got a function which calculates the position point on the wave for you.

The resource you linked uses raycasting, doesn’t it? the poster of that resource said that

Yep, I think what I said above using the 4 invisible parts will work

Part of the resource uses raycasting to find three points on the surface to rotate towards that surface.

I’m saying you can substitute the raycasting with the function that calculates the position points for you already.

--the ground orientation math
	--//Ground orientation math//--
	local down = -10*hrp.CFrame.upVector
	local r1, r2, r3 = Ray.new(hrp.CFrame:PointToWorldSpace(p1.Position), down), Ray.new(hrp.CFrame:PointToWorldSpace(p2.Position), down), Ray.new(hrp.CFrame:PointToWorldSpace(p3.Position), down)
	local o1, p1 = workspace:FindPartOnRay(r1, model)
	local o2, p2 = workspace:FindPartOnRay(r2, model)
	local o3, p3 = workspace:FindPartOnRay(r3, model)
	local upVector = (o1 and o2 and o3) == nil and bg.CFrame.UpVector or -((p1-p2):Cross(p3-p2)).Unit

--input into body gyro later on
	local rightVector = (bg.CFrame.lookVector:Cross(upVector)).Unit
	local pos = hrp.Position
	bg.CFrame = CFrame.fromMatrix(pos, rightVector, upVector)*CFrame.Angles(0, TurnSpeed*(a-d), 0)

However instead of raycasting you can just replace it with the function to find p1,p2,p3 point 1,2, and 3.

	local p1 = GerstnerWave(--something)
	local p2 = GerstnerWave(--something)
	local p3 = GerstnerWave(--something)
	local upVector =-((p1-p2):Cross(p3-p2)).Unit

This is plane maths in order to find the upvector of the plane.

I guess it’s someting like yours with getting an angle, but more concrete as you haven’t mentioned how you will get the angle and convert it to an orientation value or such.

1 Like

I’ll admit that I know Jack all about mesh deformation, but in the post I linked he was indeed using ray casting and mesh deformation and he claimed that it worked.

1 Like

Oh, thanks for giving me an example. I’m on phone right now so can’t tell if this’ll work, will tell later when I can get on pc.

Interesting. Then it might be a fault with the way am raycasting, will have to give this another look

perhaps Ill just add the angle into the orientation. Or maybe set the orientation directly to the angle, IDK will have to mess around.

Hey, am having this weird behavior
https://streamable.com/xn3908
Am I doing something wrong? Here’s how am doing it:

local portWave = GerstnerWave(v.Port.Position, script:GetAttribute("WaveLength"), script:GetAttribute("Direction"), script:GetAttribute("Steepness"), script:GetAttribute("Gravity"), speedCounter)
local starboardWave = GerstnerWave(v.Starboard.Position, script:GetAttribute("WaveLength"), script:GetAttribute("Direction"), script:GetAttribute("Steepness"), script:GetAttribute("Gravity"), speedCounter)
local bowWave = GerstnerWave(v.Bow.Position, script:GetAttribute("WaveLength"), script:GetAttribute("Direction"), script:GetAttribute("Steepness"), script:GetAttribute("Gravity"), speedCounter)
local sternWave = GerstnerWave(v.Stern.Position, script:GetAttribute("WaveLength"), script:GetAttribute("Direction"), script:GetAttribute("Steepness"), script:GetAttribute("Gravity"), speedCounter)
local down = v.CFrame.upVector * -10
local upVector = -((portWave - starboardWave):Cross(sternWave - starboardWave)).Unit
local rightVector = (v.CFrame.LookVector:Cross(upVector)).Unit
v.BodyGyro.CFrame = CFrame.fromMatrix(v.Position, -rightVector, upVector)

Sorry if its a bit hard to read. Also can you explain me what “:Cross” and “.Unit” mean? I’m using these for the first time.

As I said, it didnt work. Just tried it. Printing the surface normal of the raycast spams Vector3.new(0, 1, 0 in the output because the water is using mesh deformation. I saw your post and its interesting how its working for that guy. Here’s how am doing it:

local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Whitelist
raycastParams.FilterDescendantsInstances = {workspace.Ocean}
raycastParams.IgnoreWater = true
local direction = v.CFrame.UpVector * -10
local raycast = workspace:Raycast(v.Position, direction, raycastParams)
if raycast then
    print(raycast.Normal)
    print(raycast.Position)
end

To make sure my raycast is hitting the water I did print(raycast.Instance:GetFullName()) and it printed workspace.Ocean.Plane too. The only problem is to get the SurfaceNormal.

1 Like