tsMeshFit.cpp

Engine/source/ts/tsMeshFit.cpp

More...

Classes:

class
class

A helper class for fitting primitives (Box, Sphere, Capsule) to a triangulated mesh.

Public Functions

DefineTSShapeConstructorMethod(addCollisionDetail , bool , (S32 size, const char *type, const char *target, S32 depth, F32 merge, F32 concavity, S32 maxVerts, F32 boxMaxError, F32 sphereMaxError, F32 capsuleMaxError) , (4, 30, 30, 32, 0, 0, 0) , (size, type, target, depth, merge, concavity, maxVerts, boxMaxError, sphereMaxError, capsuleMaxError) , false , "Autofit <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> mesh primitive or set of convex hulls <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the shape geometry. Hulls " "may optionally be converted <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> boxes, spheres and/or capsules based on their " "<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">volume.\n</a>" " @param <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this detail <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">level\n</a>" " @param type one of:box , sphere , capsule , 10-dop x, 10-dop y, 10-dop z, 18- dop, " "26- dop, convex hulls. See the Shape Editor documentation <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> more details " "about these <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">types.\n</a>" " @param target geometry <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> fit collision  mesh, or the name of an object in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape\n</a>" " @param depth maximum split recursion depth(hulls only)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @param merge volume % threshold used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> merge hulls together(hulls only)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @param concavity volume % threshold used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> detect concavity(hulls only)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @param maxVerts maximum number of vertices per hull(hulls only)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @param boxMaxError max % volume difference <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> hull <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be converted <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> " "box(hulls only)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @param sphereMaxError max % volume difference <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> hull <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be converted <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> " "<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> sphere(hulls only)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @param capsuleMaxError max % volume difference <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> hull <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be converted <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> " "<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> capsule(hulls only)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @return true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> successful, false <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">otherwise\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "%this.addCollisionDetail(-1, \"box\", \"bounds\" );\n" "%this.addCollisionDetail( -1, \"convex hulls\", \"bounds\", 4, 30, 30, 32, 0, 0, 0 );\n" "%this.addCollisionDetail( -1, \"convex hulls\", \"bounds\", 4, 30, 30, 32, 50, 50, 50 );\n" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" )
DefineTSShapeConstructorMethod(addPrimitive , bool , (const char *meshName, const char *type, const char *params, TransformF txfm, const char *nodeName) , (meshName, type, params, txfm, nodeName) , false , "Add <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> mesh primitive <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape.\n</a>" "@param meshName full name (object name + detail <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a>) of the <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> mesh. If " "no detail <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> is present at the end of the name, <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> of 2 is used.< br >" "An underscore before the number at the end of the name will be interpreted as " "<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> negative sign. eg. \"MyMesh_4\" will be interpreted as \"MyMesh-4\".\n" "@param type one of: \"box\" , \"sphere\" , \"capsule\"\n" "@param params mesh primitive <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">parameters:\n</a>" "<ul>" "<li><a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> box: \"size_x size_y size_z\"</li>" "<li><a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> sphere: \"radius\"</li>" "<li><a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> capsule: \"height radius\"</li>" "</ul>" "</ul>\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@param txfm local transform offset from the node <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mesh\n</a>" "@param nodeName name of the node <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> attach the <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> mesh <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> (will change the " "object's node <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> adding <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> mesh <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> an existing object)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@return true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> successful, false <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">otherwise\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "%this.addMesh(\"Box4\", \"box\", \"2 4 2\", \"0 2 0 0 0 1 0\", \"eye\" );\n" "%this.addMesh( \"Sphere256\", \"sphere\", \"2\", \"0 0 0 0 0 1 0\", \"root\" );\n" "%this.addMesh( \"MyCapsule-1\", \"capsule\", \"2 5\", \"0 0 2 0 0 1 0\", \"base01\" );\n" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" )

Detailed Description

Public Variables

const Point3F sCornerPlanes []
const Point3F sFacePlanes []
const Point3F sXEdgePlanes []
const Point3F sYEdgePlanes []
const Point3F sZEdgePlanes []

Public Functions

DefineTSShapeConstructorMethod(addCollisionDetail , bool , (S32 size, const char *type, const char *target, S32 depth, F32 merge, F32 concavity, S32 maxVerts, F32 boxMaxError, F32 sphereMaxError, F32 capsuleMaxError) , (4, 30, 30, 32, 0, 0, 0) , (size, type, target, depth, merge, concavity, maxVerts, boxMaxError, sphereMaxError, capsuleMaxError) , false , "Autofit <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> mesh primitive or set of convex hulls <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the shape geometry. Hulls " "may optionally be converted <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> boxes, spheres and/or capsules based on their " "<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">volume.\n</a>" " @param <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this detail <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">level\n</a>" " @param type one of:box , sphere , capsule , 10-dop x, 10-dop y, 10-dop z, 18- dop, " "26- dop, convex hulls. See the Shape Editor documentation <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> more details " "about these <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">types.\n</a>" " @param target geometry <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> fit collision  mesh, or the name of an object in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape\n</a>" " @param depth maximum split recursion depth(hulls only)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @param merge volume % threshold used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> merge hulls together(hulls only)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @param concavity volume % threshold used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> detect concavity(hulls only)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @param maxVerts maximum number of vertices per hull(hulls only)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @param boxMaxError max % volume difference <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> hull <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be converted <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> " "box(hulls only)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @param sphereMaxError max % volume difference <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> hull <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be converted <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> " "<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> sphere(hulls only)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @param capsuleMaxError max % volume difference <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> hull <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be converted <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> " "<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> capsule(hulls only)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @return true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> successful, false <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">otherwise\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "%this.addCollisionDetail(-1, \"box\", \"bounds\" );\n" "%this.addCollisionDetail( -1, \"convex hulls\", \"bounds\", 4, 30, 30, 32, 0, 0, 0 );\n" "%this.addCollisionDetail( -1, \"convex hulls\", \"bounds\", 4, 30, 30, 32, 50, 50, 50 );\n" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" )

DefineTSShapeConstructorMethod(addPrimitive , bool , (const char *meshName, const char *type, const char *params, TransformF txfm, const char *nodeName) , (meshName, type, params, txfm, nodeName) , false , "Add <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> mesh primitive <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape.\n</a>" "@param meshName full name (object name + detail <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a>) of the <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> mesh. If " "no detail <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> is present at the end of the name, <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> of 2 is used.< br >" "An underscore before the number at the end of the name will be interpreted as " "<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> negative sign. eg. \"MyMesh_4\" will be interpreted as \"MyMesh-4\".\n" "@param type one of: \"box\" , \"sphere\" , \"capsule\"\n" "@param params mesh primitive <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">parameters:\n</a>" "<ul>" "<li><a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> box: \"size_x size_y size_z\"</li>" "<li><a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> sphere: \"radius\"</li>" "<li><a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> capsule: \"height radius\"</li>" "</ul>" "</ul>\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@param txfm local transform offset from the node <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mesh\n</a>" "@param nodeName name of the node <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> attach the <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> mesh <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> (will change the " "object's node <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> adding <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> mesh <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> an existing object)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@return true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> successful, false <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">otherwise\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "%this.addMesh(\"Box4\", \"box\", \"2 4 2\", \"0 2 0 0 0 1 0\", \"eye\" );\n" "%this.addMesh( \"Sphere256\", \"sphere\", \"2\", \"0 0 0 0 0 1 0\", \"root\" );\n" "%this.addMesh( \"MyCapsule-1\", \"capsule\", \"2 5\", \"0 0 2 0 0 1 0\", \"base01\" );\n" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" )

  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2012 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//-----------------------------------------------------------------------------
 23
 24#include "platform/platform.h"
 25
 26#include "console/consoleTypes.h"
 27#include "core/resourceManager.h"
 28#include "ts/tsShapeConstruct.h"
 29#include "console/engineAPI.h"
 30
 31// define macros required for ConvexDecomp headers
 32#if defined( _WIN32 ) && !defined( WIN32 )
 33#define WIN32
 34#elif defined( __MACOSX__ ) && !defined( APPLE )
 35#define APPLE
 36#endif
 37
 38#include "convexDecomp/NvFloatMath.h"
 39#include "convexDecomp/NvConvexDecomposition.h"
 40#include "convexDecomp/NvStanHull.h"
 41
 42//-----------------------------------------------------------------------------
 43static const Point3F sFacePlanes[] = {
 44   Point3F( -1.0f,  0.0f,  0.0f ),
 45   Point3F(  1.0f,  0.0f,  0.0f ),
 46   Point3F(  0.0f, -1.0f,  0.0f ),
 47   Point3F(  0.0f,  1.0f,  0.0f ),
 48   Point3F(  0.0f,  0.0f, -1.0f ),
 49   Point3F(  0.0f,  0.0f,  1.0f ),
 50};
 51
 52static const Point3F sXEdgePlanes[] = {
 53   Point3F( 0.0f, -0.7071f, -0.7071f ),
 54   Point3F( 0.0f, -0.7071f,  0.7071f ),
 55   Point3F( 0.0f,  0.7071f, -0.7071f ),
 56   Point3F( 0.0f,  0.7071f,  0.7071f ),
 57};
 58
 59static const Point3F sYEdgePlanes[] = {
 60   Point3F( -0.7071f, 0.0f, -0.7071f ),
 61   Point3F( -0.7071f, 0.0f,  0.7071f ),
 62   Point3F(  0.7071f, 0.0f, -0.7071f ),
 63   Point3F(  0.7071f, 0.0f,  0.7071f ),
 64};
 65
 66static const Point3F sZEdgePlanes[] = {
 67   Point3F( -0.7071f, -0.7071f, 0.0f ),
 68   Point3F( -0.7071f,  0.7071f, 0.0f ),
 69   Point3F(  0.7071f, -0.7071f, 0.0f ),
 70   Point3F(  0.7071f,  0.7071f, 0.0f ),
 71};
 72
 73static const Point3F sCornerPlanes[] = {
 74   Point3F( -0.5774f, -0.5774f, -0.5774f ),
 75   Point3F( -0.5774f, -0.5774f,  0.5774f ),
 76   Point3F( -0.5774f,  0.5774f, -0.5774f ),
 77   Point3F( -0.5774f,  0.5774f,  0.5774f ),
 78   Point3F(  0.5774f, -0.5774f, -0.5774f ),
 79   Point3F(  0.5774f, -0.5774f,  0.5774f ),
 80   Point3F(  0.5774f,  0.5774f, -0.5774f ),
 81   Point3F(  0.5774f,  0.5774f,  0.5774f ),
 82};
 83
 84//-----------------------------------------------------------------------------
 85
 86/** A helper class for fitting primitives (Box, Sphere, Capsule) to a triangulated mesh */
 87struct PrimFit
 88{
 89   MatrixF     mBoxTransform;
 90   Point3F     mBoxSides;
 91
 92   Point3F     mSphereCenter;
 93   F32         mSphereRadius;
 94
 95   MatrixF     mCapTransform;
 96   F32         mCapRadius;
 97   F32         mCapHeight;
 98
 99public:
100   PrimFit() :
101      mBoxTransform(true), mBoxSides(1,1,1),
102      mSphereCenter(0,0,0), mSphereRadius(1),
103      mCapTransform(true), mCapRadius(1), mCapHeight(1)
104   {
105   }
106
107   inline F32 getBoxVolume() const { return mBoxSides.x * mBoxSides.y * mBoxSides.z; }
108   inline F32 getSphereVolume() const { return 4.0f / 3.0f * M_PI * mPow( mSphereRadius, 3 ); }
109   inline F32 getCapsuleVolume() const { return 2 * M_PI * mPow( mCapRadius, 2 ) * (4.0f / 3.0f * mCapRadius + mCapHeight); }
110
111   void fitBox( U32 vertCount, const F32* verts )
112   {
113      CONVEX_DECOMPOSITION::fm_computeBestFitOBB( vertCount, verts, sizeof(F32)*3, (F32*)mBoxSides, (F32*)mBoxTransform );
114      mBoxTransform.transpose();
115   }
116
117   void fitSphere( U32 vertCount, const F32* verts )
118   {
119      mSphereRadius = CONVEX_DECOMPOSITION::fm_computeBestFitSphere( vertCount, verts, sizeof(F32)*3, (F32*)mSphereCenter );
120   }
121
122   void fitCapsule( U32 vertCount, const F32* verts )
123   {
124      CONVEX_DECOMPOSITION::fm_computeBestFitCapsule( vertCount, verts, sizeof(F32)*3, mCapRadius, mCapHeight, (F32*)mCapTransform );
125      mCapTransform.transpose();
126   }
127};
128
129class MeshFit
130{
131public:
132   enum eMeshType
133   {
134      Box = 0,
135      Sphere,
136      Capsule,
137      Hull,
138   };
139
140   struct Mesh
141   {
142      eMeshType   type;
143      MatrixF     transform;
144      TSMesh      *tsmesh;
145   };
146
147private:
148   TSShape           *mShape;    ///!< Source geometry shape
149   Vector<Point3F>   mVerts;     ///!< Source geometry verts (all meshes)
150   Vector<U32>       mIndices;   ///!< Source geometry indices (triangle lists, all meshes)
151
152   bool              mIsReady;   ///!< Flag indicating whether we are ready to fit/create meshes
153
154   Vector<Mesh>      mMeshes;    ///!< Fitted meshes
155
156   void addSourceMesh( const TSShape::Object& obj, const TSMesh* mesh );
157   TSMesh* initMeshFromFile( const String& filename ) const;
158   TSMesh* createTriMesh( F32* verts, S32 numVerts, U32* indices, S32 numTris ) const;
159   F32 maxDot( const VectorF& v ) const;
160   void fitK_DOP( const Vector<Point3F>& planes );
161
162public:
163   MeshFit(TSShape* shape) : mShape(shape), mIsReady(false) { }
164
165   void setReady() { mIsReady = true; }
166   bool isReady() const { return mIsReady; }
167
168   void initSourceGeometry( const String& target );
169
170   S32 getMeshCount() const { return mMeshes.size(); }
171   Mesh* getMesh( S32 index ) { return &(mMeshes[index]); }
172
173   // Box
174   void addBox( const Point3F& sides, const MatrixF& mat );
175   void fitOBB();
176
177   // Sphere
178   void addSphere( F32 radius, const Point3F& center );
179   void fitSphere();
180
181   // Capsule
182   void addCapsule( F32 radius, F32 height, const MatrixF& mat );
183   void fitCapsule();
184
185   // k-DOP
186   void fit10_DOP_X();
187   void fit10_DOP_Y();
188   void fit10_DOP_Z();
189   void fit18_DOP();
190   void fit26_DOP();
191
192   // Convex Hulls
193   void fitConvexHulls( U32 depth, F32 mergeThreshold, F32 concavityThreshold, U32 maxHullVerts,
194                        F32 boxMaxError, F32 sphereMaxError, F32 capsuleMaxError );
195};
196
197
198void MeshFit::initSourceGeometry( const String& target )
199{
200   mMeshes.clear();
201   mVerts.clear();
202   mIndices.clear();
203
204   if ( target.equal( "bounds", String::NoCase ) )
205   {
206      // Add all geometry in the highest detail level
207      S32 dl = 0;
208      S32 ss = mShape->details[dl].subShapeNum;
209      if ( ss < 0 )
210         return;
211
212      S32 od = mShape->details[dl].objectDetailNum;
213      S32 start = mShape->subShapeFirstObject[ss];
214      S32 end   = start + mShape->subShapeNumObjects[ss];
215
216      for ( S32 i = start; i < end; i++ )
217      {
218         const TSShape::Object &obj = mShape->objects[i];
219         const TSMesh* mesh = ( od < obj.numMeshes ) ? mShape->meshes[obj.startMeshIndex + od] : NULL;
220         if ( mesh )
221            addSourceMesh( obj, mesh );
222      }
223   }
224   else
225   {
226      // Add highest detail mesh from this object
227      S32 objIndex = mShape->findObject( target );
228      if ( objIndex == -1 )
229         return;
230
231      const TSShape::Object &obj = mShape->objects[objIndex];
232      for ( S32 i = 0; i < obj.numMeshes; i++ )
233      {
234         const TSMesh* mesh = mShape->meshes[obj.startMeshIndex + i];
235         if ( mesh )
236         {
237            addSourceMesh( obj, mesh );
238            break;
239         }
240      }
241   }
242
243   mIsReady = ( !mVerts.empty() && !mIndices.empty() );
244}
245
246void MeshFit::addSourceMesh( const TSShape::Object& obj, const TSMesh* mesh )
247{
248   // Add indices
249   S32 indicesBase = mIndices.size();
250   for ( S32 i = 0; i < mesh->mPrimitives.size(); i++ )
251   {
252      const TSDrawPrimitive& draw = mesh->mPrimitives[i];
253      if ( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles )
254      {
255         mIndices.merge( &mesh->mIndices[draw.start], draw.numElements );
256      }
257      else
258      {
259         U32 idx0 = mesh->mIndices[draw.start + 0];
260         U32 idx1;
261         U32 idx2 = mesh->mIndices[draw.start + 1];
262         U32 *nextIdx = &idx1;
263         for ( S32 j = 2; j < draw.numElements; j++ )
264         {
265            *nextIdx = idx2;
266            nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1);
267            idx2 = mesh->mIndices[draw.start + j];
268            if ( idx0 == idx1 || idx0 == idx2 || idx1 == idx2 )
269               continue;
270
271            mIndices.push_back( idx0 );
272            mIndices.push_back( idx1 );
273            mIndices.push_back( idx2 );
274         }
275      }
276   }
277
278   // Offset indices for already added verts
279   for ( S32 j = indicesBase; j < mIndices.size(); j++ )
280      mIndices[j] += mVerts.size();
281
282   // Add verts
283   S32 count, stride;
284   U8* pVert;
285
286   if ( mesh->mVertexData.isReady() && mesh->mVerts.size() == 0 )
287   {
288      count = mesh->mVertexData.size();
289      stride = mesh->mVertexData.vertSize();
290      pVert = (U8*)mesh->mVertexData.address();
291   }
292   else
293   {
294      count = mesh->mVerts.size();
295      stride = sizeof(Point3F);
296      pVert = (U8*)mesh->mVerts.address();
297   }
298
299   MatrixF objMat;
300   mShape->getNodeWorldTransform( obj.nodeIndex, &objMat );
301
302   mVerts.reserve( mVerts.size() + count );
303   for ( S32 j = 0; j < count; j++, pVert += stride )
304   {
305      mVerts.increment();
306      objMat.mulP( *(Point3F*)pVert, &mVerts.last() );
307   }
308}
309
310TSMesh* MeshFit::initMeshFromFile( const String& filename ) const
311{
312   // Open the source shape file and make a copy of the mesh
313   Resource<TSShape> hShape = ResourceManager::get().load(filename);
314   if (!bool(hShape) || !((TSShape*)hShape)->meshes.size())
315   {
316      Con::errorf("TSShape::createMesh: Could not load source mesh from %s", filename.c_str());
317      return NULL;
318   }
319
320   TSMesh* srcMesh = ((TSShape*)hShape)->meshes[0];
321   return mShape->copyMesh( srcMesh );
322}
323
324TSMesh* MeshFit::createTriMesh( F32* verts, S32 numVerts, U32* indices, S32 numTris ) const
325{
326   TSMesh* mesh = mShape->copyMesh( NULL );
327   mesh->numFrames = 1;
328   mesh->numMatFrames = 1;
329   mesh->vertsPerFrame = numVerts;
330   mesh->setFlags(0);
331   mesh->mNumVerts = numVerts;
332
333   mesh->mIndices.reserve( numTris * 3 );
334   for ( S32 i = 0; i < numTris; i++ )
335   {
336      mesh->mIndices.push_back( indices[i*3 + 0] );
337      mesh->mIndices.push_back( indices[i*3 + 2] );
338      mesh->mIndices.push_back( indices[i*3 + 1] );
339   }
340
341   mesh->mVerts.set( verts, numVerts );
342
343   // Compute mesh normals
344   mesh->mNorms.setSize( mesh->mVerts.size() );
345   for (S32 iNorm = 0; iNorm < mesh->mNorms.size(); iNorm++)
346      mesh->mNorms[iNorm] = Point3F::Zero;
347
348   // Sum triangle normals for each vertex
349   for (S32 iInd = 0; iInd < mesh->mIndices.size(); iInd += 3)
350   {
351      // Compute the normal for this triangle
352      S32 idx0 = mesh->mIndices[iInd + 0];
353      S32 idx1 = mesh->mIndices[iInd + 1];
354      S32 idx2 = mesh->mIndices[iInd + 2];
355
356      const Point3F& v0 = mesh->mVerts[idx0];
357      const Point3F& v1 = mesh->mVerts[idx1];
358      const Point3F& v2 = mesh->mVerts[idx2];
359
360      Point3F n;
361      mCross(v2 - v0, v1 - v0, &n);
362      n.normalize();    // remove this to use 'weighted' normals (large triangles will have more effect)
363
364      mesh->mNorms[idx0] += n;
365      mesh->mNorms[idx1] += n;
366      mesh->mNorms[idx2] += n;
367   }
368
369   // Normalize the vertex normals (this takes care of averaging the triangle normals)
370   for (S32 iNorm = 0; iNorm < mesh->mNorms.size(); iNorm++)
371      mesh->mNorms[iNorm].normalize();
372
373   // Set some dummy UVs
374   mesh->mTverts.setSize( numVerts );
375   for ( S32 j = 0; j < mesh->mTverts.size(); j++ )
376      mesh->mTverts[j].set( 0, 0 );
377
378   // Add a single triangle-list primitive
379   mesh->mPrimitives.increment();
380   mesh->mPrimitives.last().start = 0;
381   mesh->mPrimitives.last().numElements = mesh->mIndices.size();
382   mesh->mPrimitives.last().matIndex =  TSDrawPrimitive::Triangles |
383                                       TSDrawPrimitive::Indexed |
384                                       TSDrawPrimitive::NoMaterial;
385
386   mesh->createTangents( mesh->mVerts, mesh->mNorms);
387   mesh->mEncodedNorms.set( NULL,0 );
388
389   return mesh;
390}
391
392F32 MeshFit::maxDot( const VectorF& v ) const
393{
394   F32 maxDot = -FLT_MAX;
395   for ( S32 i = 0; i < mVerts.size(); i++ )
396      maxDot = getMax( maxDot, mDot( v, mVerts[i] ) );
397   return maxDot;
398}
399
400//---------------------------
401// Best-fit oriented bounding box
402void MeshFit::addBox( const Point3F& sides, const MatrixF& mat )
403{
404   TSMesh* mesh = initMeshFromFile( TSShapeConstructor::getCubeShapePath() );
405   if ( !mesh )
406      return;
407
408   if (mesh->mVerts.size() > 0)
409   {
410      for (S32 i = 0; i < mesh->mVerts.size(); i++)
411      {
412         Point3F v = mesh->mVerts[i];
413         v.convolve(sides);
414         mesh->mVerts[i] = v;
415      }
416
417      mesh->mVertexData.setReady(false);
418   }
419   else
420   {
421      for (S32 i = 0; i < mesh->mVertexData.size(); i++)
422      {
423         TSMesh::__TSMeshVertexBase &vdata = mesh->mVertexData.getBase(i);
424         Point3F v = vdata.vert();
425         v.convolve(sides);
426         vdata.vert(v);
427      }
428   }
429
430   mesh->computeBounds();
431
432   mMeshes.increment();
433   mMeshes.last().type = MeshFit::Box;
434   mMeshes.last().transform = mat;
435   mMeshes.last().tsmesh = mesh;
436}
437
438void MeshFit::fitOBB()
439{
440   PrimFit primFitter;
441   primFitter.fitBox( mVerts.size(), (F32*)mVerts.address() );
442   addBox( primFitter.mBoxSides, primFitter.mBoxTransform );
443}
444
445//---------------------------
446// Best-fit sphere
447void MeshFit::addSphere( F32 radius, const Point3F& center )
448{
449   TSMesh* mesh = initMeshFromFile( TSShapeConstructor::getSphereShapePath() );
450   if ( !mesh )
451      return;
452
453   for ( S32 i = 0; i < mesh->mVertexData.size(); i++ )
454   {
455      TSMesh::__TSMeshVertexBase &vdata = mesh->mVertexData.getBase(i);
456      Point3F v = vdata.vert();
457      vdata.vert( v * radius );
458   }
459   mesh->computeBounds();
460
461   mMeshes.increment();
462   MeshFit::Mesh& lastMesh = mMeshes.last();
463   lastMesh.type = MeshFit::Sphere;
464   lastMesh.transform.identity();
465   lastMesh.transform.setPosition(center);
466   lastMesh.tsmesh = mesh;
467}
468
469void MeshFit::fitSphere()
470{
471   PrimFit primFitter;
472   primFitter.fitSphere( mVerts.size(), (F32*)mVerts.address() );
473   addSphere( primFitter.mSphereRadius, primFitter.mSphereCenter );
474}
475
476//---------------------------
477// Best-fit capsule
478void MeshFit::addCapsule( F32 radius, F32 height, const MatrixF& mat )
479{
480   TSMesh* mesh = initMeshFromFile( TSShapeConstructor::getCapsuleShapePath() );
481   if ( !mesh )
482      return;
483
484   // Translate and scale the mesh verts
485   height = mMax( 0, height );
486   F32 offset = ( height / ( 2 * radius ) ) - 0.5f;
487   for ( S32 i = 0; i < mesh->mVertexData.size(); i++ )
488   {
489      Point3F v = mesh->mVertexData.getBase(i).vert();
490      v.y += ( ( v.y > 0 ) ? offset : -offset );
491      mesh->mVertexData.getBase(i).vert( v * radius );
492   }
493   mesh->computeBounds();
494
495   mMeshes.increment();
496   mMeshes.last().type = MeshFit::Capsule;
497   mMeshes.last().transform = mat;
498   mMeshes.last().tsmesh = mesh;
499}
500
501void MeshFit::fitCapsule()
502{
503   PrimFit primFitter;
504   primFitter.fitCapsule( mVerts.size(), (F32*)mVerts.address() );
505   addCapsule( primFitter.mCapRadius, primFitter.mCapHeight, primFitter.mCapTransform );
506}
507
508//---------------------------
509// Best-fit k-discrete-oriented-polytope (where k is the number of axis-aligned planes)
510
511// All faces + 4 edges (aligned to X axis) of the unit cube
512void MeshFit::fit10_DOP_X()
513{
514   Vector<Point3F> planes;
515   planes.setSize( 10 );
516   dCopyArray( planes.address(), sFacePlanes, 6 );
517   dCopyArray( planes.address()+6, sXEdgePlanes, 4 );
518   fitK_DOP( planes );
519}
520
521// All faces + 4 edges (aligned to Y axis)  of the unit cube
522void MeshFit::fit10_DOP_Y()
523{
524   Vector<Point3F> planes;
525   planes.setSize( 10 );
526   dCopyArray( planes.address(), sFacePlanes, 6 );
527   dCopyArray( planes.address()+6, sYEdgePlanes, 4 );
528   fitK_DOP( planes );
529}
530
531// All faces + 4 edges (aligned to Z axis)  of the unit cube
532void MeshFit::fit10_DOP_Z()
533{
534   Vector<Point3F> planes;
535   planes.setSize( 10 );
536   dCopyArray( planes.address(), sFacePlanes, 6 );
537   dCopyArray( planes.address()+6, sZEdgePlanes, 4 );
538   fitK_DOP( planes );
539}
540
541// All faces and edges of the unit cube
542void MeshFit::fit18_DOP()
543{
544   Vector<Point3F> planes;
545   planes.setSize( 18 );
546   dCopyArray( planes.address(), sFacePlanes, 6 );
547   dCopyArray( planes.address()+6, sXEdgePlanes, 4 );
548   dCopyArray( planes.address()+10, sYEdgePlanes, 4 );
549   dCopyArray( planes.address()+14, sZEdgePlanes, 4 );
550   fitK_DOP( planes );
551}
552
553// All faces, edges and corners of the unit cube
554void MeshFit::fit26_DOP()
555{
556   Vector<Point3F> planes;
557   planes.setSize( 26 );
558   dCopyArray( planes.address(), sFacePlanes, 6 );
559   dCopyArray( planes.address()+6, sXEdgePlanes, 4 );
560   dCopyArray( planes.address()+10, sYEdgePlanes, 4 );
561   dCopyArray( planes.address()+14, sZEdgePlanes, 4 );
562   dCopyArray( planes.address()+18, sCornerPlanes, 8 );
563   fitK_DOP( planes );
564}
565
566void MeshFit::fitK_DOP( const Vector<Point3F>& planes )
567{
568   // Push the planes up against the mesh
569   Vector<F32> planeDs;
570   for ( S32 i = 0; i < planes.size(); i++ )
571      planeDs.push_back( maxDot( planes[i] ) );
572
573   // Collect the intersection points of any 3 planes that lie inside
574   // the maximum distances found above
575   Vector<Point3F> points;
576   for ( S32 i = 0; i < planes.size()-2; i++ )
577   {
578      for ( S32 j = i+1; j < planes.size()-1; j++ )
579      {
580         for ( S32 k = j+1; k < planes.size(); k++ )
581         {
582            Point3F v23 = mCross( planes[j], planes[k] );
583            F32 denom = mDot( planes[i], v23 );
584            if ( denom == 0 )
585               continue;
586
587            Point3F v31 = mCross( planes[k], planes[i] );
588            Point3F v12 = mCross( planes[i], planes[j] );
589            Point3F p = ( planeDs[i]*v23 + planeDs[j]*v31 + planeDs[k]*v12 ) / denom;
590
591            // Ignore intersection points outside the volume
592            // described by the planes
593            bool addPoint = true;
594            for ( S32 n = 0; n < planes.size(); n++ )
595            {
596               if ( ( mDot( p, planes[n] ) - planeDs[n] ) > 0.005f )
597               {
598                  addPoint = false;
599                  break;
600               }
601            }
602
603            if ( addPoint )
604               points.push_back( p );
605         }
606      }
607   }
608
609   // Create a convex hull from the point set
610   CONVEX_DECOMPOSITION::HullDesc hd;
611   hd.mVcount        = points.size();
612   hd.mVertices      = (F32*)points.address();
613   hd.mVertexStride  = sizeof(Point3F);
614   hd.mMaxVertices   = 64;
615   hd.mSkinWidth     = 0.0f;
616
617   CONVEX_DECOMPOSITION::HullLibrary hl;
618   CONVEX_DECOMPOSITION::HullResult result;
619   hl.CreateConvexHull( hd, result );
620
621   // Create TSMesh from convex hull
622   mMeshes.increment();
623   MeshFit::Mesh& lastMesh = mMeshes.last();
624   lastMesh.type = MeshFit::Hull;
625   lastMesh.transform.identity();
626   lastMesh.tsmesh = createTriMesh(result.mOutputVertices, result.mNumOutputVertices,
627                              result.mIndices, result.mNumFaces );
628   lastMesh.tsmesh->computeBounds();
629}
630
631//---------------------------
632// Best-fit set of convex hulls
633void MeshFit::fitConvexHulls( U32 depth, F32 mergeThreshold, F32 concavityThreshold, U32 maxHullVerts,
634                              F32 boxMaxError, F32 sphereMaxError, F32 capsuleMaxError )
635{
636   const F32 SkinWidth      = 0.0f;
637   const F32 SplitThreshold = 2.0f;
638
639   CONVEX_DECOMPOSITION::iConvexDecomposition *ic = CONVEX_DECOMPOSITION::createConvexDecomposition();
640
641   for ( S32 i = 0; i < mIndices.size(); i += 3 )
642   {
643      ic->addTriangle(  (F32*)mVerts[mIndices[i]],
644                        (F32*)mVerts[mIndices[i+1]],
645                        (F32*)mVerts[mIndices[i+2]] );
646   }
647
648   ic->computeConvexDecomposition(
649      SkinWidth,
650      depth,
651      maxHullVerts,
652      concavityThreshold,
653      mergeThreshold,
654      SplitThreshold,
655      true,
656      false,
657      false );
658
659   // Add a TSMesh for each hull
660   for ( S32 i = 0; i < ic->getHullCount(); i++ )
661   {
662      CONVEX_DECOMPOSITION::ConvexHullResult result;
663      ic->getConvexHullResult( i, result );
664
665      eMeshType meshType = MeshFit::Hull;
666
667      // Check if we can use a box, sphere or capsule primitive for this hull
668      if (( boxMaxError > 0 ) || ( sphereMaxError > 0 ) || ( capsuleMaxError > 0 ))
669      {
670         // Compute error between actual mesh and fitted primitives
671         F32 meshVolume = CONVEX_DECOMPOSITION::fm_computeMeshVolume( result.mVertices, result.mTcount, result.mIndices );
672         PrimFit primFitter;
673
674         F32 boxError = 100.0f, sphereError = 100.0f, capsuleError = 100.0f;
675         if ( boxMaxError > 0 )
676         {
677            primFitter.fitBox( result.mVcount, result.mVertices );
678            boxError = 100.0f * ( 1.0f - ( meshVolume / primFitter.getBoxVolume() ) );
679         }
680         if ( sphereMaxError > 0 )
681         {
682            primFitter.fitSphere( result.mVcount, result.mVertices );
683            sphereError = 100.0f * ( 1.0f - ( meshVolume / primFitter.getSphereVolume() ) );
684         }
685         if ( capsuleMaxError > 0 )
686         {
687            primFitter.fitCapsule( result.mVcount, result.mVertices );
688            capsuleError = 100.0f * ( 1.0f - ( meshVolume / primFitter.getCapsuleVolume() ) );
689         }
690
691         // Use the primitive type with smallest error less than the respective
692         // max error, or Hull if none
693         F32 minError = FLT_MAX;
694         if ( ( boxError < boxMaxError ) && ( boxError < minError ) )
695         {
696            meshType = MeshFit::Box;
697            minError = boxError;
698         }
699         if ( ( sphereError < sphereMaxError ) && ( sphereError < minError ) )
700         {
701            meshType = MeshFit::Sphere;
702            minError = sphereError;
703         }
704         if ( ( capsuleError < capsuleMaxError ) && ( capsuleError < minError ) )
705         {
706            meshType = MeshFit::Capsule;
707            minError = capsuleError;
708         }
709
710         if ( meshType == MeshFit::Box )
711            addBox( primFitter.mBoxSides, primFitter.mBoxTransform );
712         else if ( meshType == MeshFit::Sphere )
713            addSphere( primFitter.mSphereRadius, primFitter.mSphereCenter );
714         else if ( meshType == MeshFit::Capsule )
715            addCapsule( primFitter.mCapRadius, primFitter.mCapHeight, primFitter.mCapTransform );
716         // else fall through to Hull processing
717      }
718
719      if ( meshType == MeshFit::Hull )
720      {
721         // Create TSMesh from convex hull
722         mMeshes.increment();
723         MeshFit::Mesh& lastMesh = mMeshes.last();
724         lastMesh.type = MeshFit::Hull;
725         lastMesh.transform.identity();
726         lastMesh.tsmesh = createTriMesh(result.mVertices, result.mVcount, result.mIndices, result.mTcount);
727         lastMesh.tsmesh->computeBounds();
728      }
729   }
730
731   CONVEX_DECOMPOSITION::releaseConvexDecomposition( ic );
732}
733
734//-----------------------------------------------------------------------------
735DefineTSShapeConstructorMethod( addPrimitive, bool, ( const char* meshName, const char* type, const char* params, TransformF txfm, const char* nodeName ),,
736   ( meshName, type, params, txfm, nodeName ), false,
737   "Add a new mesh primitive to the shape.\n"
738   "@param meshName full name (object name + detail size) of the new mesh. If "
739      "no detail size is present at the end of the name, a value of 2 is used.<br>"
740      "An underscore before the number at the end of the name will be interpreted as "
741      "a negative sign. eg. \"MyMesh_4\" will be interpreted as \"MyMesh-4\".\n"
742   "@param type one of: \"box\", \"sphere\", \"capsule\"\n"
743   "@param params mesh primitive parameters:\n"
744      "<ul>"
745         "<li>for box: \"size_x size_y size_z\"</li>"
746         "<li>for sphere: \"radius\"</li>"
747         "<li>for capsule: \"height radius\"</li>"
748      "</ul>"
749   "</ul>\n"
750   "@param txfm local transform offset from the node for this mesh\n"
751   "@param nodeName name of the node to attach the new mesh to (will change the "
752   "object's node if adding a new mesh to an existing object)\n"
753   "@return true if successful, false otherwise\n\n"
754   "@tsexample\n"
755   "%this.addMesh( \"Box4\", \"box\", \"2 4 2\", \"0 2 0 0 0 1 0\", \"eye\" );\n"
756   "%this.addMesh( \"Sphere256\", \"sphere\", \"2\", \"0 0 0 0 0 1 0\", \"root\" );\n"
757   "%this.addMesh( \"MyCapsule-1\", \"capsule\", \"2 5\", \"0 0 2 0 0 1 0\", \"base01\" );\n"
758   "@endtsexample\n" )
759{
760   MeshFit fit( mShape );
761   if ( !dStricmp( type, "box" ) )
762   {
763      // Parse box parameters
764      Point3F sides;
765      if ( dSscanf( params, "%g %g %g", &sides.x, &sides.y, &sides.z ) == 3 )
766      {
767         fit.addBox( sides, MatrixF::Identity );
768         fit.setReady();
769      }
770   }
771   else if ( !dStricmp( type, "sphere" ) )
772   {
773      // Parse sphere parameters
774      F32 radius;
775      if ( dSscanf( params, "%g", &radius ) == 1)
776      {
777         fit.addSphere( radius, Point3F::Zero );
778         fit.setReady();
779      }
780   }
781   else if ( !dStricmp( type, "capsule" ) )
782   {
783      // Parse capsule parameters
784      F32 radius, height;
785      if ( dSscanf( params, "%g %g", &radius, &height ) == 1)
786      {
787         fit.addCapsule( radius, height, MatrixF::Identity );
788         fit.setReady();
789      }
790   }
791
792   if ( !fit.isReady() )
793   {
794      Con::errorf( "TSShapeConstructor::addPrimitive: Invalid params: '%s' for type '%s'",
795         params, type );
796      return false;
797   }
798
799   TSMesh* mesh = fit.getMesh( 0 )->tsmesh;
800   MatrixF mat( txfm.getMatrix() );
801
802   // Transform the mesh vertices
803   if ( mesh->mVertexData.isReady() && mesh->mVerts.size() == 0 )
804   {
805      for (S32 i = 0; i < mesh->mVertexData.size(); i++)
806      {
807         TSMesh::__TSMeshVertexBase &vdata = mesh->mVertexData.getBase(i);
808         Point3F v;
809         mat.mulP( vdata.vert(), &v );
810         vdata.vert( v );
811      }
812   }
813   else
814   {
815      for (S32 i = 0; i < mesh->mVerts.size(); i++)
816      {
817         Point3F v(mesh->mVerts[i]);
818         mat.mulP( v, &mesh->mVerts[i] );
819      }
820   }
821
822   // Add the mesh to the shape at the right node
823   mShape->addMesh( mesh, meshName );
824
825   S32 dummy;
826   String objName = String::GetTrailingNumber( meshName, dummy );
827   setObjectNode( objName, nodeName );
828
829   mShape->init();
830
831   ADD_TO_CHANGE_SET();
832   return true;
833}}
834
835DefineTSShapeConstructorMethod( addCollisionDetail, bool, ( S32 size, const char* type, const char* target, S32 depth, F32 merge, F32 concavity, S32 maxVerts, F32 boxMaxError, F32 sphereMaxError, F32 capsuleMaxError ), ( 4, 30, 30, 32, 0, 0, 0 ),
836   ( size, type, target, depth, merge, concavity, maxVerts, boxMaxError, sphereMaxError, capsuleMaxError ), false,
837   "Autofit a mesh primitive or set of convex hulls to the shape geometry. Hulls "
838   "may optionally be converted to boxes, spheres and/or capsules based on their "
839   "volume.\n"
840   "@param size size for this detail level\n"
841   "@param type one of: box, sphere, capsule, 10-dop x, 10-dop y, 10-dop z, 18-dop, "
842      "26-dop, convex hulls. See the Shape Editor documentation for more details "
843      "about these types.\n"
844   "@param target geometry to fit collision mesh(es) to; either \"bounds\" (for the "
845      "whole shape), or the name of an object in the shape\n"
846   "@param depth maximum split recursion depth (hulls only)\n"
847   "@param merge volume % threshold used to merge hulls together (hulls only)\n"
848   "@param concavity volume % threshold used to detect concavity (hulls only)\n"
849   "@param maxVerts maximum number of vertices per hull (hulls only)\n"
850   "@param boxMaxError max % volume difference for a hull to be converted to a "
851      "box (hulls only)\n"
852   "@param sphereMaxError max % volume difference for a hull to be converted to "
853      "a sphere (hulls only)\n"
854   "@param capsuleMaxError max % volume difference for a hull to be converted to "
855      "a capsule (hulls only)\n"
856   "@return true if successful, false otherwise\n\n"
857   "@tsexample\n"
858   "%this.addCollisionDetail( -1, \"box\", \"bounds\" );\n"
859   "%this.addCollisionDetail( -1, \"convex hulls\", \"bounds\", 4, 30, 30, 32, 0, 0, 0 );\n"
860   "%this.addCollisionDetail( -1, \"convex hulls\", \"bounds\", 4, 30, 30, 32, 50, 50, 50 );\n"
861   "@endtsexample\n" )
862{
863   MeshFit fit( mShape );
864   fit.initSourceGeometry( target );
865   if ( !fit.isReady() )
866   {
867      Con::errorf( "TSShapeConstructor::addCollisionDetail: Failed to initialise mesh fitter "
868         "using target: %s", target );
869      return false;
870   }
871
872   if ( !dStricmp( type, "box" ) )
873      fit.fitOBB();
874   else if ( !dStricmp( type, "sphere" ) )
875      fit.fitSphere();
876   else if ( !dStricmp( type, "capsule" ) )
877      fit.fitCapsule();
878   else if ( !dStricmp( type, "10-dop x" ) )
879      fit.fit10_DOP_X();
880   else if ( !dStricmp( type, "10-dop y" ) )
881      fit.fit10_DOP_Y();
882   else if ( !dStricmp( type, "10-dop z" ) )
883      fit.fit10_DOP_Z();
884   else if ( !dStricmp( type, "18-dop" ) )
885      fit.fit18_DOP();
886   else if ( !dStricmp( type, "26-dop" ) )
887      fit.fit26_DOP();
888   else if ( !dStricmp( type, "convex hulls" ) )
889   {
890      fit.fitConvexHulls( depth, merge, concavity, maxVerts,
891                           boxMaxError, sphereMaxError, capsuleMaxError );
892   }
893   else
894   {
895      Con::errorf( "TSShape::addCollisionDetail: Invalid type: '%s'", type );
896      return false;
897   }
898
899   // Now add the fitted meshes to the shape:
900   // - primitives (box, sphere, capsule) need their own node (with appropriate
901   //   transform set) so that we can use the mesh bounds to compute the real
902   //   collision primitive at load time without having to examine the geometry.
903   // - convex meshes may be added at the default node, with identity transform
904   // - since all meshes are in the same detail level, they all get a unique
905   //   object name
906
907   const String colNodeName( String::ToString( "Col%d", size ) );
908
909   // Add the default node with identity transform
910   S32 nodeIndex = mShape->findNode( colNodeName );
911   if ( nodeIndex == -1 )
912   {
913      addNode( colNodeName, "" );
914   }
915   else
916   {
917      MatrixF mat;
918      mShape->getNodeWorldTransform( nodeIndex, &mat );
919      if ( !mat.isIdentity() )
920         setNodeTransform( colNodeName, TransformF::Identity );
921   }
922
923   // Add the meshes to the shape => 
924   for ( S32 i = 0; i < fit.getMeshCount(); i++ )
925   {
926      MeshFit::Mesh* mesh = fit.getMesh( i );
927
928      // Determine a unique name for this mesh
929      String objName;
930      switch ( mesh->type )
931      {
932         case MeshFit::Box:      objName = "ColBox";       break;
933         case MeshFit::Sphere:   objName = "ColSphere";    break;
934         case MeshFit::Capsule:  objName = "ColCapsule";   break;
935         default:                objName = "ColConvex";    break;
936      }
937
938      for ( S32 suffix = i; suffix != 0; suffix /= 26 )
939         objName += ('A' + ( suffix % 26 ) );
940      String meshName = objName + String::ToString( "%d", size );
941
942      mShape->addMesh( mesh->tsmesh, meshName );
943
944      // Add a node for this object if needed (non-identity transform)
945      if ( mesh->transform.isIdentity() )
946      {
947         mShape->setObjectNode( objName, colNodeName );
948      }
949      else
950      {
951         addNode( meshName, colNodeName, TransformF( mesh->transform ) );
952         mShape->setObjectNode( objName, meshName );
953      }
954   }
955
956   mShape->init();
957
958   ADD_TO_CHANGE_SET();
959   return true;
960}}
961