Jump to content

Aligning an object to a surface normal


Recommended Posts

Im trying to align a building properly on a sphere surface and am having trouble with the code behind it. I know the ProxMine does something like this when its deployed, but thats in source code. I looked there and cant seem to figure out the script conversion. Heres whats in source:


// Align to deployed surface normal
  MatrixF mat( true );
  MathUtils::getMatrixFromUpVector( normal, &mat );
  mat.setPosition( pos + normal * mObjBox.minExtents.z );

  delta.pos = pos;
  delta.posVec.set(0, 0, 0);

  ShapeBase::setTransform( mat );


Anyone have any ideas on how to do this?

Link to comment
Share on other sites

Alright - in theory, you could raycast and get the surface normal of the point of intersection like this:

%ray = VectorScale(%ray, 1000);
%end = VectorAdd(%start, %ray);
// only care about the following object types
%searchMasks = $TypeMasks::TerrainObjectType | $TypeMasks::StaticTSObjectType | 
$TypeMasks::InteriorObjectType | $TypeMasks::ShapeBaseObjectType | $TypeMasks::StaticObjectType;
// search!
%scanTarg = ContainerRayCast( %start, %end, %searchMasks);
if (%scanTarg)
    %obj = getWord(%scanTarg, 0);
    while (%obj.class $= "barrier")
        // Get the X,Y,Z position of where we clicked
        %pos = getWords(%scanTarg, 1, 3);
        %restart = VectorNormalize(VectorSub(%end, %pos));
        %pos = VectorAdd(%pos, %restart);
        %scanTarg = ContainerRayCast( %pos, %end, %searchMasks);
        %obj = getWord(%scanTarg, 0);
    // Get the X,Y,Z position of where we clicked
    %pos = getWords(%scanTarg, 1, 3);
    // Get the normal of the location we clicked on
    %norm = getWords(%scanTarg, 4, 6);
    // do something with normal data

This works for placing decals - as seen in the RTS Prototype. I guess you'd have to translate the normal into some sort of rotation and apply it to your model.....

Link to comment
Share on other sites

No, but in engine/math/mathUtils.cpp there is a getMatrixFromUpVector() that might be a good start. In that same file there is a getAnglesFromVector() function that might be helpful too.

Have to expose them to the console, then figure out how to convert either the matrix or the angles to a transform.....

Link to comment
Share on other sites

Stick this at the end of mathTypes.cpp (for some reason all the mathutil console function hooks are here so you won't need to add any headers):


DefineConsoleFunction( VectorGetMatrixFromUpVector, TransformF, ( VectorF vec ),,
   "@Create a matrix from the up vector.\n\n"
   "@param VectorF (x,y,z) up vector.\n"
   "@return TransformF.\n"
   "@ingroup Vectors" )
   MatrixF outMat;
   MathUtils::getMatrixFromUpVector(vec, &outMat);
   return outMat;


The data that comes back will be in the "TransformF" format which means words 0-2 are position and 3-6 are rotation (axis angle). In script you can do something like this:


%mat = VectorGetMatrixFromUpVector(%normal)
%outTrans = %object.getPosition() SPC getWords(%mat, 3, 6);


All of your placed objects will have 0 rotation around the normal (because the normal vector obviously contains no info about that rotation) like the mines. If you want to rotate about the normal it should be a simple matrix multiplication like this:


void WorldEditorSelection::rotate(const EulerF &rot)
   for( iterator iter = begin(); iter != end(); ++ iter )
      SceneObject* object = dynamic_cast< SceneObject* >( *iter );
      if( !object )
         MatrixF mat = object->getTransform();
         MatrixF transform(rot);


But again you need to expose some math helpers to script (or just attach a function like this to sceneobject and expose THAT to script).

The script-end handling of object rotation isn't very good in general. I'd like to have euler angles exposed (along site the current axis angle interface) and some rotation functions that can do both of the relative rotations the editor handles (world and local yaw/pitch/roll).

Link to comment
Share on other sites

Thank you very much for that! Currently Im using this code:


%normal = getWords(%building.getTransform(), 0, 2);
%normalN = vectorNormalize(%normal);
%axis = VectorCross(%normalN, "0 0 1");
%angle = mACos(vectorDot(%normalN, "0 0 1"));
%rot = %axis SPC %angle;

       // get the world position of the click
       %pos = getWords(%building.getTransform(), 0, 2);

// Reposition the building
%building.setTransform(%pos SPC %rot);


The first line I have with the normal just using the position is because the object im trying to align to is a sphere at 0 0 0, so I thought it was safe to assume that the vector from 0 0 0 to the building position would be just the position.

My code seems to work fine for the most part but the alignment is a bit off and when a building is closer to the bottom of the sphere it seems to get squished and disfigured. Not sure whats causing that but Ill try using your method above to see if it works better and fixes that issue.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Create New...