tsMeshFit.cpp
Engine/source/ts/tsMeshFit.cpp
Classes:
class
class
class
A helper class for fitting primitives (Box, Sphere, Capsule) to a triangulated mesh.
Public Variables
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