You have to simulate the projectile motion of the bomb (which I assume you're already doing?), and raycast ahead in the direction that it is moving, but I can see how the stream distance can be a problem. If you've moved the logic to the server, then this is a way to check whether you are inside of the submarine. Sorry for the long-winded explanation, but you did say you are clueless

So, here you go!

----

The first thing you want to do is make sure your submarine is spawned with a zero angle. Any position is fine, and either a

ClientStaticObject or

StaticObject can be used. I used a StaticObject in this example.

The function

StaticObject:GetBoundingBox() returns two opposite corners of an axis-aligned bounding box (AABB) for that object.

You can see them marked here with yellow circles:

`function DrawBoundingCorners(obj, color)`

local u, v = obj:GetBoundingBox()

Render:DrawCircle(Render:WorldToScreen(u), 10, color)

Render:DrawCircle(Render:WorldToScreen(v), 10, color)

end

If you take those two corners, and permutate their x, y, and z values, you will get the remaining six corners of a BB. By drawing lines between the eight corners, you can visualize the whole BB:

`function DrawBoundingBox(obj, color)`

local u, v = obj:GetBoundingBox()

local corners = {

Vector3(u.x, u.y, u.z),

Vector3(u.x, u.y, v.z),

Vector3(v.x, u.y, v.z),

Vector3(v.x, u.y, u.z),

Vector3(u.x, v.y, u.z),

Vector3(u.x, v.y, v.z),

Vector3(v.x, v.y, v.z),

Vector3(v.x, v.y, u.z),

}

Render:DrawLine(corners[1], corners[2], color)

Render:DrawLine(corners[2], corners[3], color)

Render:DrawLine(corners[3], corners[4], color)

Render:DrawLine(corners[4], corners[1], color)

Render:DrawLine(corners[1], corners[5], color)

Render:DrawLine(corners[2], corners[6], color)

Render:DrawLine(corners[3], corners[7], color)

Render:DrawLine(corners[4], corners[8], color)

Render:DrawLine(corners[5], corners[6], color)

Render:DrawLine(corners[6], corners[7], color)

Render:DrawLine(corners[7], corners[8], color)

Render:DrawLine(corners[8], corners[5], color)

end

The 'axis-aligned' part means that the edges of the BB will always be parallel to the axes of the game world, regardless of the orientation of the object.

If you rotate the submarine, you can see how the BB expands to fit the object, but its orientation does not change. Only when the object is axis-aligned (like when set with a zero angle) is the BB also object-aligned, which more accurately represents the shape of the object.

How can you make sure that the BB is always object-aligned? One way to do this is by recording the BB data while the object is axis-aligned. You can then "playback" the data at any other angle.

Notice that the BB corners, like all positions, are relative to the game world. You want them to be relative to the object. To convert them, subtract the object's position from the BB corners:

`function GetBoundingBoxRelative(obj)`

local position = obj:GetPosition()

local u,v = obj:GetBoundingBox()

return u - position, v - position

end

With the submarine, you get the following points. These tell you how far the corners are from the object's base position:

`Vector3(-66.159668, -41.867691, -265.626831)`

Vector3( 66.140381, 69.942322, 255.328735)

You can save these values to a table:

`boxes = {`

['km07.submarine.eez/key014_02-a.lod'] = {Vector3(-66.16, -41.87, -265.63), Vector3(66.14, 69.94, 255.33)}

}

and use them to draw a BB relative to the object:

`function DrawBoundingBoxRelative(obj, color)`

local model = obj:GetModel()

local position = obj:GetPosition()

local angle = obj:GetAngle()

local u = boxes[model][1]

local v = boxes[model][2]

local corners = {

position + angle * Vector3(u.x, u.y, u.z),

position + angle * Vector3(u.x, u.y, v.z),

position + angle * Vector3(v.x, u.y, v.z),

position + angle * Vector3(v.x, u.y, u.z),

position + angle * Vector3(u.x, v.y, u.z),

position + angle * Vector3(u.x, v.y, v.z),

position + angle * Vector3(v.x, v.y, v.z),

position + angle * Vector3(v.x, v.y, u.z),

}

Render:DrawLine(corners[1], corners[2], color)

Render:DrawLine(corners[2], corners[3], color)

Render:DrawLine(corners[3], corners[4], color)

Render:DrawLine(corners[4], corners[1], color)

Render:DrawLine(corners[1], corners[5], color)

Render:DrawLine(corners[2], corners[6], color)

Render:DrawLine(corners[3], corners[7], color)

Render:DrawLine(corners[4], corners[8], color)

Render:DrawLine(corners[5], corners[6], color)

Render:DrawLine(corners[6], corners[7], color)

Render:DrawLine(corners[7], corners[8], color)

Render:DrawLine(corners[8], corners[5], color)

end

If you look at the BB at this point, you're probably saying that it's not even close to submarine sized or shaped. That's because the BB expands to fit every irregular part of the submarine. That's not really helpful. This is where I recommend that you use convert your bounding box into an bounding ellipsoid. I find that it is easier to check whether a point lies inside of an ellipsoid than in a box. Submarines are also conveniently ellipsoid-shaped. If you didn't already know, an ellipsoid is a 3D ellipse; a squashed sphere. The concept here is similar to that of a Vector3.Distance check, except instead of being confined to one radius, you can have a radius that continuously changes depending upon the length, width, and height of the object.

What the following does is determine the parameters for an ellipsoid based on the BB:

If you want a circumscribed ellipsoid, divide the edge lengths by sqrt(2):

`ellipsoids = {}`

for model,box in pairs(boxes) do

local a = (box[2].x - box[1].x) / math.sqrt(2)

local b = (box[2].y - box[1].y) / math.sqrt(2)

local c = (box[2].z - box[1].z) / math.sqrt(2)

ellipsoids[model] = {a, b, c}

end

If you want an inscribed ellipsoid, divide the edge lengths by 2:

`ellipsoids = {}`

for model,box in pairs(boxes) do

local a = (box[2].x - box[1].x) / 2

local b = (box[2].y - box[1].y) / 2

local c = (box[2].z - box[1].z) / 2

ellipsoids[model] = {a, b, c}

end

I used an inscribed ellipsoid.

You could use these values to draw a 3D model of an ellipsoid, but it's a easier to draw and visualize with a 2D ellipse on each plane:

`function DrawBoundingEllipsoidRelative(obj, resolution, color)`

local model = obj:GetModel()

local position = obj:GetPosition()

local angle = obj:GetAngle()

local u = position + angle * boxes[model][1]

local v = position + angle * boxes[model][2]

local origin = math.lerp(u, v, 0.5)

local a = ellipsoids[model][1]

local b = ellipsoids[model][2]

local c = ellipsoids[model][3]

local ellipses = {{}, {}, {}}

for theta = 0, (2 * math.pi), (2 * math.pi) / resolution do

table.insert(ellipses[1], origin + angle * Vector3(a * math.cos(theta), b * math.sin(theta), 0))

table.insert(ellipses[2], origin + angle * Vector3(a * math.cos(theta), 0, c * math.sin(theta)))

table.insert(ellipses[3], origin + angle * Vector3(0, b * math.sin(theta), c * math.cos(theta)))

end

for i,u in ipairs(ellipses) do

for j,v in ipairs(u) do

if u[j+1] then Render:DrawLine(v, u[j+1], color) end

end

end

end

This is starting to look more submarine like, except that it isn't quite centered and it's obviously too big. To fix this, you can adjust the box data until the ellipsoid better matches the size and shape of the submarine. I came up with the following numbers:

`boxes = {`

['km07.submarine.eez/key014_02-a.lod'] = {Vector3(-45, -42, -280), Vector3(45, 5, 260)}

}

It's not perfect, and you're never going to get a perfect fit unless you use multiple boxes. If that's something you want to do, let me know, but I think this should be good enough.

If you wanted to get crazy, you could look at a 'superellipsoid', which would allow you to use more irregular shapes:

But an ellipsoid should be good enough.

Visualizing the data is great, but since the whole point of this is to figure out if a position is inside of the submarine, you can do that by evaluating the standard function for an ellipsoid:

`function PositionIsInsideStaticObject(position, obj)`

local model = obj:GetModel()

local pos = obj:GetPosition()

local angle = obj:GetAngle()

local u = pos + angle * boxes[model][1]

local v = pos + angle * boxes[model][2]

local origin = math.lerp(u, v, 0.5)

local coord = -angle * (position - origin)

local x_term = (coord.x / ellipsoids[model][1])^2

local y_term = (coord.y / ellipsoids[model][2])^2

local z_term = (coord.z / ellipsoids[model][3])^2

if x_term + y_term + z_term > 1 then

return false

else

return true

end

end

----

I hope this helps and isn't too confusing. Please let me know if you have questions, or if something doesn't work as advertised.

Here is the whole script that I used:

http://pastebin.com/qvpw85qG