Skip to content

Commit

Permalink
reduce bounding box work - calc 2 corners not 8.
Browse files Browse the repository at this point in the history
  • Loading branch information
Dunbaratu committed Jul 18, 2019
1 parent baa363f commit fb08141
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 37 deletions.
19 changes: 13 additions & 6 deletions doc/source/structures/vessels/bounds.rst
Original file line number Diff line number Diff line change
Expand Up @@ -743,14 +743,21 @@ on whether item A is touching item B.

To get a rotated box like is needed for kOS's needs, where its tightly
snug against the object in question, kOS has to go a bit more low-level
and look at the actual meshes that make up the object, and look at
and look at the actual meshes that make up a part, and look at
*their* bounding boxes, which are aligned in the mesh's own locally
rotated XYZ axes, rather than world axes. Some ugly transforms
of each of the 8 vertices of the mesh bounding box Unity knows about are
needed, and there isn't really a good way to do this without running a
loop across all vertices of the box, which is what kOS does internally.
For the whole vessel's bounding box, this means doing those 8 vertices
per part, on every part on the ship.
of the min and max corners of the mesh bounding box Unity knows about are
needed. This isn't *that* expensive, but it is if you have IPU set to
1000 and try to do this operation 1000 times per update tick.

The real expense comes when trying to get a vessel's bounding box
with ``Vessel:BOUNDS``, rather than just one part's bounding box
with ``Part:BOUNDS``. To get the Vessel's bounds, it performs
the work of getting a part's bounds, on each part on the vessel,
in order to find the union of all those boxes into a bigger box.

Calling ``Part:BOUNDS`` repeatedly in your script isn't really that
bad. It's just calling ``Vessel:BOUNDS`` repeatedly that's a bad idea.

Why then is it faster to re-use the BOUNDS suffix?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
16 changes: 8 additions & 8 deletions doc/source/structures/vessels/part.rst
Original file line number Diff line number Diff line change
Expand Up @@ -228,14 +228,14 @@ These are the generic properties every PART has. You can obtain a list of values
give your script some idea of the extents of the part's shape - how
wide, long, and tall it is.

It can be slightly expensive in terms of CPU time to keep calling
this suffix over and over, as kOS has to perform some work to build
this structure. If you need to keep looking at a part's bounds again
and again in a loop, and you know that part's shape isn't going to be
changing (i.e. you're not going to extend a solar panel or something
like that), then it's better for you to call this ``:BOUNDS`` suffix
just once at the top, storing the result in a variable that you use in
the loop.
kOS has to perform some internal work to build this structure, so it
is *slightly* expensive to call it (but not *that* bad). If you
need to keep looking at the same part's bounds again and again in a
loop, and you know that part's shape isn't going to be changing
(i.e. you're not going to extend a solar panel or something like
that), then it's better for you to call this ``:BOUNDS`` suffix
just once at the top, storing the result in a variable that you use
in the loop.

More detailed information is found on the documentation page for
:struct:`Bounds`.
Expand Down
8 changes: 4 additions & 4 deletions doc/source/structures/vessels/vessel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,10 @@ Vessels are also :ref:`Orbitable<orbitable>`, and as such have all the associate
wide, long, and tall it is.

It is rather expensive in terms of CPU time to call this suffix.
(Calling :attr:`Part:BOUNDS` on ONE part on the ship is itself a
*little* expensive, and this has to perform that same work on
every part on the ship, finding the bounding box that would
surround all the parts.) Because of that expense, kOS **forces**
(Calling :attr:`Part:BOUNDS` on ONE part on the ship isn't that
expensive, but this has to repeat that same work for every part
on the ship, finding the bounding box that would surround all
the parts.) Because of that expense, kOS **forces**
your script to give up its remaining instructions this update when
you call this (It forces the equivalent of doing a ``WAIT 0.``
right after you call it). This is to discourage you from
Expand Down
37 changes: 18 additions & 19 deletions src/kOS/Suffixed/Part/PartValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,29 +85,28 @@ public BoundsValue GetBoundsValue()
// Part meshes could be scaled as well as rotated (the mesh might describe a
// part that's 1 meter wide while the real part is 2 meters wide, and has a scale of 2x
// encoded into its transform to do this). Because of this, the only really
// reliable way to get the real shape is to let the transform do its work on all 6 corners
// of the bounding box, transforming them with the mesh's transform, then back-calculating
// from that world-space result back into the part's own reference frame to get the bounds
// relative to the part.
// reliable way to get the real shape is to let the transform do its work on the two
// opposite diagonal corners of the bounding box, first transforming them with the mesh's
// transform, then back-calculating from that world-space result back into the part's own
// reference frame to get the bounds relative to the part.
Console.WriteLine("eraseme: starting a mesh work.");
Vector3 center = bounds.center;

// This triple-nested loop visits all 8 corners of the box:
for (int signX = -1; signX <= 1; signX += 2) // -1, then +1
for (int signY = -1; signY <= 1; signY += 2) // -1, then +1
for (int signZ = -1; signZ <= 1; signZ += 2) // -1, then +1
{
Vector3 corner = center + new Vector3(signX * bounds.extents.x, signY * bounds.extents.y, signZ * bounds.extents.z);
Console.WriteLine("eraseme: corner = " + corner);
Vector3 worldCorner = mesh.transform.TransformPoint(corner);
Console.WriteLine("eraseme:worldCorner = " + worldCorner);
Vector3 partCorner = rotateYToZ * Part.transform.InverseTransformPoint(worldCorner);
Console.WriteLine("eraseme: partCorner = " + partCorner);
// Works on just the two diagonally opposite corners of the box, which will guarantee the other 6
// corners are placed correctly too, "for free" without going through the math on all of them:
for (int sign = -1; sign <= 1; sign += 2) // -1, then +1
{
Vector3 corner = center + new Vector3(sign * bounds.extents.x, sign * bounds.extents.y, sign * bounds.extents.z);
Console.WriteLine("eraseme: corner = " + corner);
Vector3 worldCorner = mesh.transform.TransformPoint(corner);
Console.WriteLine("eraseme:worldCorner = " + worldCorner);
Vector3 partCorner = rotateYToZ * Part.transform.InverseTransformPoint(worldCorner);
Console.WriteLine("eraseme: partCorner = " + partCorner);

// Stretches the bounds we're making (which started at size zero in all axes),
// just big enough to include this corner:
unionBounds.Encapsulate(partCorner);
}
// Stretches the bounds we're making (which started at size zero in all axes),
// just big enough to include this corner:
unionBounds.Encapsulate(partCorner);
}
}
Console.WriteLine("eraseme: unionBounds.min x=" + unionBounds.min.x + " y=" + unionBounds.min.y + " z=" + unionBounds.min.z);
Console.WriteLine("eraseme: unionBounds.max x=" + unionBounds.max.x + " y=" + unionBounds.max.y + " z=" + unionBounds.max.z);
Expand Down

0 comments on commit fb08141

Please sign in to comment.