/** * @file llphysicsshapebuilder.cpp * @brief Generic system to convert LL(Physics)VolumeParams to physics shapes * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" #include "llphysicsshapebuilderutil.h" #include "llmeshrepository.h" bool LLPhysicsVolumeParams::hasDecomposition() const { if (!isMeshSculpt()) { return false; } LLUUID mesh_id = getSculptID(); if (mesh_id.isNull()) { return false; } LLModel::Decomposition* decomp = gMeshRepo.getDecomposition(mesh_id); return decomp != NULL; } /* static */ void LLPhysicsShapeBuilderUtil::determinePhysicsShape( const LLPhysicsVolumeParams& volume_params, const LLVector3& scale, PhysicsShapeSpecification& specOut) { const LLProfileParams& profile_params = volume_params.getProfileParams(); const LLPathParams& path_params = volume_params.getPathParams(); specOut.mScale = scale; const F32 avgScale = ( scale[VX] + scale[VY] + scale[VZ] )/3.0f; // count the scale elements that are small S32 min_size_counts = 0; for (S32 i = 0; i < 3; ++i) { if (scale[i] < SHAPE_BUILDER_CONVEXIFICATION_SIZE) { ++min_size_counts; } } const bool profile_complete = ( profile_params.getBegin() <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT/avgScale ) && ( profile_params.getEnd() >= (1.0f - SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT/avgScale) ); const bool path_complete = ( path_params.getBegin() <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT/avgScale ) && ( path_params.getEnd() >= (1.0f - SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT/avgScale) ); const bool simple_params = ( volume_params.getHollow() <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_HOLLOW/avgScale ) && ( fabs(path_params.getShearX()) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_SHEAR/avgScale ) && ( fabs(path_params.getShearY()) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_SHEAR/avgScale ) && ( !volume_params.isMeshSculpt() && !volume_params.isSculpt() ); if (simple_params && profile_complete) { // Try to create an implicit shape or convexified bool no_taper = ( fabs(path_params.getScaleX() - 1.0f) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_TAPER/avgScale ) && ( fabs(path_params.getScaleY() - 1.0f) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_TAPER/avgScale ); bool no_twist = ( fabs(path_params.getTwistBegin()) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_TWIST/avgScale ) && ( fabs(path_params.getTwistEnd()) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_TWIST/avgScale); // Box if( ( profile_params.getCurveType() == LL_PCODE_PROFILE_SQUARE ) && ( path_params.getCurveType() == LL_PCODE_PATH_LINE ) && no_taper && no_twist ) { specOut.mType = PhysicsShapeSpecification::BOX; if ( path_complete ) { return; } else { // Side lengths specOut.mScale[VX] = llmax( scale[VX], SHAPE_BUILDER_MIN_GEOMETRY_SIZE ); specOut.mScale[VY] = llmax( scale[VY], SHAPE_BUILDER_MIN_GEOMETRY_SIZE ); specOut.mScale[VZ] = llmax( scale[VZ] * (path_params.getEnd() - path_params.getBegin()), SHAPE_BUILDER_MIN_GEOMETRY_SIZE ); specOut.mCenter.set( 0.f, 0.f, 0.5f * scale[VZ] * ( path_params.getEnd() + path_params.getBegin() - 1.0f ) ); return; } } // Sphere if( path_complete && ( profile_params.getCurveType() == LL_PCODE_PROFILE_CIRCLE_HALF ) && ( path_params.getCurveType() == LL_PCODE_PATH_CIRCLE ) && ( fabs(volume_params.getTaper()) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_TAPER/avgScale ) && no_twist ) { if ( ( scale[VX] == scale[VZ] ) && ( scale[VY] == scale[VZ] ) ) { // perfect sphere specOut.mType = PhysicsShapeSpecification::SPHERE; specOut.mScale = scale; return; } else if (min_size_counts > 1) { // small or narrow sphere -- we can boxify for (S32 i=0; i<3; ++i) { if (specOut.mScale[i] < SHAPE_BUILDER_CONVEXIFICATION_SIZE) { // reduce each small dimension size to split the approximation errors specOut.mScale[i] *= 0.75f; } } specOut.mType = PhysicsShapeSpecification::BOX; return; } } // Cylinder if( (scale[VX] == scale[VY]) && ( profile_params.getCurveType() == LL_PCODE_PROFILE_CIRCLE ) && ( path_params.getCurveType() == LL_PCODE_PATH_LINE ) && ( volume_params.getBeginS() <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT/avgScale ) && ( volume_params.getEndS() >= (1.0f - SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT/avgScale) ) && no_taper ) { if (min_size_counts > 1) { // small or narrow sphere -- we can boxify for (S32 i=0; i<3; ++i) { if (specOut.mScale[i] < SHAPE_BUILDER_CONVEXIFICATION_SIZE) { // reduce each small dimension size to split the approximation errors specOut.mScale[i] *= 0.75f; } } specOut.mType = PhysicsShapeSpecification::BOX; } else { specOut.mType = PhysicsShapeSpecification::CYLINDER; F32 length = (volume_params.getPathParams().getEnd() - volume_params.getPathParams().getBegin()) * scale[VZ]; specOut.mScale[VY] = specOut.mScale[VX]; specOut.mScale[VZ] = length; // The minus one below fixes the fact that begin and end range from 0 to 1 not -1 to 1. specOut.mCenter.set( 0.f, 0.f, 0.5f * (volume_params.getPathParams().getBegin() + volume_params.getPathParams().getEnd() - 1.f) * scale[VZ] ); } return; } } if ( (min_size_counts == 3 ) // Possible dead code here--who wants to take it out? || (path_complete && profile_complete && ( path_params.getCurveType() == LL_PCODE_PATH_LINE ) && (min_size_counts > 1 ) ) ) { // it isn't simple but // we might be able to convexify this shape if the path and profile are complete // or the path is linear and both path and profile are complete --> we can boxify it specOut.mType = PhysicsShapeSpecification::BOX; specOut.mScale = scale; return; } // Special case for big, very thin objects - bump the small dimensions up to the COLLISION_TOLERANCE if (min_size_counts == 1 // One dimension is small && avgScale > 3.f) // ... but others are fairly large { for (S32 i = 0; i < 3; ++i) { specOut.mScale[i] = llmax( specOut.mScale[i], COLLISION_TOLERANCE ); } } if ( volume_params.shouldForceConvex() ) { // Server distinguishes between convex of a prim vs isSculpt, but we don't care. specOut.mType = PhysicsShapeSpecification::USER_CONVEX; } // Make a simpler convex shape if we can. else if (volume_params.isConvex() // is convex || min_size_counts > 1 ) // two or more small dimensions { specOut.mType = PhysicsShapeSpecification::PRIM_CONVEX; } else if (volume_params.isMeshSculpt()) { // Check overall dimensions, not individual triangles. if (scale.mV[0] < SHAPE_BUILDER_USER_MESH_CONVEXIFICATION_SIZE || scale.mV[1] < SHAPE_BUILDER_USER_MESH_CONVEXIFICATION_SIZE || scale.mV[2] < SHAPE_BUILDER_USER_MESH_CONVEXIFICATION_SIZE ) { if (volume_params.hasDecomposition()) { specOut.mType = PhysicsShapeSpecification::USER_MESH; } else { // Server distinguishes between user-specified or default convex mesh, vs server's thin-triangle override, but we don't. specOut.mType = PhysicsShapeSpecification::PRIM_CONVEX; } } else { specOut.mType = PhysicsShapeSpecification::USER_MESH; } } else if ( volume_params.isSculpt() ) { specOut.mType = PhysicsShapeSpecification::SCULPT; } else // Resort to mesh { specOut.mType = PhysicsShapeSpecification::PRIM_MESH; } }