/** * @file llcape.cpp * @brief LLVOCloth class implementation * * Copyright (c) 2005-2007, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. */ #include "llviewerprecompiledheaders.h" #include "llcape.h" #include "llglheaders.h" #include "llsphere.h" #include "llrand.h" #include "llvoavatar.h" #include "llagent.h" #include "llviewerobject.h" #include "llimagegl.h" #include "llviewerimagelist.h" #include "llviewercontrol.h" #include "llflexibleobject.h" const int _X_ = 0; const int _Y_ = 1; const int _Z_ = 2; const F32 ONE_HALF = 0.5f; const F32 STRAND_VISUALIZATION_ANCHOR_BALL_RADIUS = 0.01f; //----------------------------------------------- // constructor //----------------------------------------------- LLVOCloth::LLVOCloth(const LLUUID &id, const LLPCode type, LLViewerRegion *regionp) : LLViewerObject(id, type, regionp) { mAttributes.mTextureFileName = CLOTH_DEFAULT_TEXTURE_FILENAME; mAttributes.mTextureIndex = CLOTH_DEFAULT_TEXTURE_INDEX; mAttributes.mNumStrands = CLOTH_DEFAULT_NUM_STRANDS; mAttributes.mNumSegments = CLOTH_DEFAULT_NUM_SEGMENTS; mAttributes.mVisualizeStrands = CLOTH_DEFAULT_VISUALIZE_STRANDS; mAttributes.mVisualizeAvCollisionSphere = CLOTH_DEFAULT_VISUALIZE_COLLISION_SPHERE; mAttributes.mWidth = CLOTH_DEFAULT_WIDTH; mAttributes.mLength = CLOTH_DEFAULT_LENGTH; mAttributes.mPitch = CLOTH_DEFAULT_PITCH; mAttributes.mGravity = CLOTH_DEFAULT_GRAVITY; mAttributes.mTension = CLOTH_DEFAULT_TENSION; mAttributes.mAirFriction = CLOTH_DEFAULT_AIR_FRICTION; mAttributes.mWindSensitivity = CLOTH_DEFAULT_WIND_SENSITIVITY; mAttributes.mCircleWrapAmount = CLOTH_DEFAULT_CIRCLE_WRAP_AMOUNT; mAttributes.mUsingAvatarCollisionSphere = CLOTH_DEFAULT_USING_AVATAR_COLLISION_SPHERE; //mAttributes.mAvatarCollisionSpherePositionOffset = CLOTH_DEFAULT_AVATAR_COLLISION_SPHERE_POSITION_OFFSET; mAttributes.mAvatarCollisionSphereRadius = CLOTH_DEFAULT_AVATAR_COLLISION_SPHERE_RADIUS; mAttributes.mAvatarCollisionSphereRightOffset = CLOTH_DEFAULT_AVATAR_COLLISION_SPHERE_RIGHT_OFFSET; mAttributes.mAvatarCollisionSphereUpOffset = CLOTH_DEFAULT_AVATAR_COLLISION_SPHERE_UP_OFFSET; mAttributes.mAvatarCollisionSphereForwardOffset = CLOTH_DEFAULT_AVATAR_COLLISION_SPHERE_FORWARD_OFFSET; generateStrands(); }//------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // this method is only called when attributes of the cloth are set, // and so it is not so important to try optimized it. //------------------------------------------------------------------------------ void LLVOCloth::generateStrands() { LLFlexibleObjectData strandAttributes; bool oddNumStrands = (bool)( mAttributes.mNumStrands % 2 ); F32 piOver180 = F_PI / 180.0f; F32 x = 0.0f; F32 y = 0.0f; F32 startAngle = -mAttributes.mCircleWrapAmount * 180.0f; F32 angleDelta = ( mAttributes.mCircleWrapAmount * 360.0f ) / (F32)( mAttributes.mNumStrands - 1 ); F32 subWidth = mAttributes.mWidth / (F32)( mAttributes.mNumStrands - 1 ); F32 angle = 0.0f; if ( mAttributes.mNumStrands > 2 ) { angle = startAngle; } //------------------------------------------- // displace x and y to start the process of // iterating through the positional offsets //------------------------------------------- if ( ! oddNumStrands ) { y += subWidth * ONE_HALF; } int half = mAttributes.mNumStrands / 2; for ( int s=0; s 2 ) { angle += angleDelta; } F32 r = angle * piOver180; x -= subWidth * sin(r); y -= subWidth * cos(r); } if ( mAttributes.mNumStrands > 2 ) { angle = startAngle; } else { angle = 0.0f; } for ( int s=0; s 2 ) { angle += angleDelta; } F32 r = angle * piOver180; F32 dX = subWidth * sin(r); F32 dY = subWidth * cos(r); x += dX; y += dY; //-------------------------------------------------- // set anchor directions //-------------------------------------------------- LLVector3 perpendicular; perpendicular.setVec( -dY, dX, 0.0f ); perpendicular.normVec(); //F32 pitchRadian = mAttributes.mPitch * F_PI_BY_TWO; /*strandAttributes.mAnchorDirection.setVec ( perpendicular.mV[_X_] * cos( pitchRadian ), perpendicular.mV[_Y_] * cos( pitchRadian ), sin( pitchRadian ) );*/ //strandAttributes.mAnchorDirection.normVec(); // just to be sure... /*strandAttributes.mTension = mAttributes.mTension; strandAttributes.mSimulateLOD = mAttributes.mNumSegments; strandAttributes.mGravity = mAttributes.mGravity; strandAttributes.mAirFriction = mAttributes.mAirFriction; strandAttributes.mWindSensitivity = mAttributes.mWindSensitivity; strandAttributes.mUsingCollisionSphere = mAttributes.mUsingAvatarCollisionSphere;*/ } }//------------------------------------------------------------------------------ //--------------------------------------------------------------------------------- void LLVOCloth::update() { if ( mAttributes.mUsingAvatarCollisionSphere ) { LLVector3 offset; offset.setVec ( mAttributes.mAvatarCollisionSphereForwardOffset, // in this strange world, forward is along X (I think that's kinda wierd) mAttributes.mAvatarCollisionSphereRightOffset, mAttributes.mAvatarCollisionSphereUpOffset ); //------------------------------------------------------ // rotate into avatar space //------------------------------------------------------ offset *= gAgent.getAvatarObject()->getRenderRotation(); mAvatarCollisionSpherePosition = gAgent.getAvatarObject()->getRenderPosition() + offset; } if ( mParent ) { // update the attachment of the strands to their parent object, and let them flop around! for ( int s=0; ssetParentPositionAndRotationDirectly( getRenderPosition(), getRenderRotation() ); //mStrand[s]->update(); if ( mAttributes.mUsingAvatarCollisionSphere ) { // it's not efficient to create the exact same collision // sphere for every strand, but it'll do for the time being mStrand[s]->setCollisionSphere( mAvatarCollisionSpherePosition, mAttributes.mAvatarCollisionSphereRadius ); } } } }//------------------------------------------------------------------ //------------------------------------------------------------------ void LLVOCloth::render() { if ( mAttributes.mTextureFileName != "not_specified" ) { LLViewerImage* theTexture = gImageList.getImage( LLUUID( gViewerArt.getString( mAttributes.mTextureFileName ) ), MIPMAP_FALSE, TRUE); if ( theTexture ) { /*if ( mAttributes.mVisualizeAvCollisionSphere ) { for ( int s=0; srenderCollisionSphere(); } }*/ if ( mAttributes.mVisualizeStrands ) { for ( int s=0; sgetAnchorPosition().mV[_X_], mStrand[s]->getAnchorPosition().mV[_Y_], mStrand[s]->getAnchorPosition().mV[_Z_] ); glScalef ( STRAND_VISUALIZATION_ANCHOR_BALL_RADIUS, STRAND_VISUALIZATION_ANCHOR_BALL_RADIUS, STRAND_VISUALIZATION_ANCHOR_BALL_RADIUS ); gSphere.render(); glPopMatrix(); //-------------------------------- // draw the strand //-------------------------------- //mStrand[s]->render(); //-------------------------------------------- // draw the segments between the strands //-------------------------------------------- if ( s > 0 ) { //-------------------------------- // draw the top edge... //-------------------------------- glBegin( GL_LINES ); glVertex3fv( mStrand[s-1]->getAnchorPosition().mV ); glVertex3fv( mStrand[s ]->getAnchorPosition().mV ); glEnd(); //-------------------------------------------- // draw the rest of the segments //-------------------------------------------- for ( int i=0; igetNodePosition(i).mV ); glVertex3fv( mStrand[s ]->getNodePosition(i).mV ); glEnd(); } } } } //-------------------------------------------------------- // now, render the cape itself //-------------------------------------------------------- glColor4fv( LLColor4( 1.0f, 1.0f, 1.0f, 1.0f ).mV ); for ( int s=1; sgetAnchorPosition(); rightBottom = mStrand[ s ]->getAnchorPosition(); } else { leftBottom = mStrand[ s-1 ]->getNodePosition( i-1 ); rightBottom = mStrand[ s ]->getNodePosition( i-1 ); } leftTop = mStrand[ s-1 ]->getNodePosition( i ); rightTop = mStrand[ s ]->getNodePosition( i ); //gGLSTexture.set(); //set render state to use texture LLViewerImage::bindTexture(theTexture); glBegin( GL_TRIANGLE_STRIP ); glTexCoord2f( x0, y0 ); glVertex3fv( leftBottom.mV ); glTexCoord2f( x1, y0 ); glVertex3fv( rightBottom.mV ); glTexCoord2f( x0, y1 ); glVertex3fv( leftTop.mV ); glEnd(); glBegin( GL_TRIANGLE_STRIP ); glTexCoord2f( x1, y0 ); glVertex3fv( rightBottom.mV ); glTexCoord2f( x1, y1 ); glVertex3fv( rightTop.mV ); glTexCoord2f( x0, y1 ); glVertex3fv( leftTop.mV ); glEnd(); theTexture->unbindTexture(0, GL_TEXTURE_2D); } } } } }//------------------------------------------------------------------ //------------------------------------------------------------------ void LLVOCloth::setAttributes( LLClothAttributes a ) { mAttributes.mTextureFileName = a.mTextureFileName; mAttributes.mTextureIndex = a.mTextureIndex; mAttributes.mNumSegments = a.mNumSegments; mAttributes.mNumStrands = a.mNumStrands; mAttributes.mVisualizeStrands = a.mVisualizeStrands; mAttributes.mVisualizeAvCollisionSphere = a.mVisualizeAvCollisionSphere; mAttributes.mWidth = a.mWidth; mAttributes.mLength = a.mLength; mAttributes.mPitch = a.mPitch; mAttributes.mTension = a.mTension; mAttributes.mGravity = a.mGravity; mAttributes.mAirFriction = a.mAirFriction; mAttributes.mWindSensitivity = a.mWindSensitivity; mAttributes.mCircleWrapAmount = a.mCircleWrapAmount; mAttributes.mUsingAvatarCollisionSphere = a.mUsingAvatarCollisionSphere; //mAttributes.mAvatarCollisionSpherePositionOffset = a.mAvatarCollisionSpherePositionOffset; mAttributes.mAvatarCollisionSphereRadius = a.mAvatarCollisionSphereRadius; mAttributes.mAvatarCollisionSphereRightOffset = a.mAvatarCollisionSphereRightOffset; mAttributes.mAvatarCollisionSphereUpOffset = a.mAvatarCollisionSphereUpOffset; mAttributes.mAvatarCollisionSphereForwardOffset = a.mAvatarCollisionSphereForwardOffset; if ( mAttributes.mTextureIndex < CLOTH_MIN_TEXTURE_INDEX ){ mAttributes.mTextureIndex = CLOTH_MIN_TEXTURE_INDEX; } else if ( mAttributes.mTextureIndex > CLOTH_MAX_TEXTURE_INDEX ){ mAttributes.mTextureIndex = CLOTH_MAX_TEXTURE_INDEX; } if ( mAttributes.mPitch < CLOTH_MIN_PITCH ){ mAttributes.mPitch = CLOTH_MIN_PITCH; } else if ( mAttributes.mPitch > CLOTH_MAX_PITCH ){ mAttributes.mPitch = CLOTH_MAX_PITCH; } if ( mAttributes.mWidth < CLOTH_MIN_WIDTH ){ mAttributes.mWidth = CLOTH_MIN_WIDTH; } else if ( mAttributes.mWidth > CLOTH_MAX_WIDTH ){ mAttributes.mWidth = CLOTH_MAX_WIDTH; } if ( mAttributes.mLength < CLOTH_MIN_LENGTH ){ mAttributes.mLength = CLOTH_MIN_LENGTH; } else if ( mAttributes.mLength > CLOTH_MAX_LENGTH ){ mAttributes.mLength = CLOTH_MAX_LENGTH; } if ( mAttributes.mNumSegments < CLOTH_MIN_SEGMENTS ){ mAttributes.mNumSegments = CLOTH_MIN_SEGMENTS; } else if ( mAttributes.mNumSegments > CLOTH_MAX_SEGMENTS ){ mAttributes.mNumSegments = CLOTH_MAX_SEGMENTS; } if ( mAttributes.mNumStrands < CLOTH_MIN_STRANDS ){ mAttributes.mNumStrands = CLOTH_MIN_STRANDS; } else if ( mAttributes.mNumStrands > CLOTH_MAX_STRANDS ){ mAttributes.mNumStrands = CLOTH_MAX_STRANDS; } if ( mAttributes.mTension < CLOTH_MIN_TENSION ){ mAttributes.mTension = CLOTH_MIN_TENSION; } else if ( mAttributes.mTension > CLOTH_MAX_TENSION ){ mAttributes.mTension = CLOTH_MAX_TENSION; } if ( mAttributes.mGravity < CLOTH_MIN_GRAVITY ){ mAttributes.mGravity = CLOTH_MIN_GRAVITY; } else if ( mAttributes.mGravity > CLOTH_MAX_GRAVITY ){ mAttributes.mGravity = CLOTH_MAX_GRAVITY; } if ( mAttributes.mAirFriction < CLOTH_MIN_AIR_FRICTION ){ mAttributes.mAirFriction = CLOTH_MIN_AIR_FRICTION; } else if ( mAttributes.mAirFriction > CLOTH_MAX_AIR_FRICTION ){ mAttributes.mAirFriction = CLOTH_MAX_AIR_FRICTION; } if ( mAttributes.mWindSensitivity < CLOTH_MIN_WIND_SENSITIVITY ){ mAttributes.mWindSensitivity = CLOTH_MIN_WIND_SENSITIVITY;} else if ( mAttributes.mWindSensitivity > CLOTH_MAX_WIND_SENSITIVITY ){ mAttributes.mWindSensitivity = CLOTH_MAX_WIND_SENSITIVITY;} if ( mAttributes.mCircleWrapAmount < CLOTH_MIN_CIRCLE_WRAP_AMOUNT ){ mAttributes.mCircleWrapAmount = CLOTH_MIN_CIRCLE_WRAP_AMOUNT;} else if ( mAttributes.mCircleWrapAmount > CLOTH_MAX_CIRCLE_WRAP_AMOUNT ){ mAttributes.mCircleWrapAmount = CLOTH_MAX_CIRCLE_WRAP_AMOUNT;} if ( mAttributes.mAvatarCollisionSphereRadius < CLOTH_MIN_AVATAR_COLLISION_SPHERE_RADIUS ){ mAttributes.mAvatarCollisionSphereRadius = CLOTH_MIN_AVATAR_COLLISION_SPHERE_RADIUS;} else if ( mAttributes.mAvatarCollisionSphereRadius > CLOTH_MAX_AVATAR_COLLISION_SPHERE_RADIUS ){ mAttributes.mAvatarCollisionSphereRadius = CLOTH_MAX_AVATAR_COLLISION_SPHERE_RADIUS;} if ( mAttributes.mAvatarCollisionSphereRightOffset < CLOTH_MIN_AVATAR_COLLISION_SPHERE_RIGHT_OFFSET ){ mAttributes.mAvatarCollisionSphereRightOffset = CLOTH_MIN_AVATAR_COLLISION_SPHERE_RIGHT_OFFSET;} else if ( mAttributes.mAvatarCollisionSphereRightOffset > CLOTH_MAX_AVATAR_COLLISION_SPHERE_RIGHT_OFFSET ){ mAttributes.mAvatarCollisionSphereRightOffset = CLOTH_MAX_AVATAR_COLLISION_SPHERE_RIGHT_OFFSET;} if ( mAttributes.mAvatarCollisionSphereUpOffset < CLOTH_MIN_AVATAR_COLLISION_SPHERE_UP_OFFSET ){ mAttributes.mAvatarCollisionSphereUpOffset = CLOTH_MIN_AVATAR_COLLISION_SPHERE_UP_OFFSET;} else if ( mAttributes.mAvatarCollisionSphereUpOffset > CLOTH_MAX_AVATAR_COLLISION_SPHERE_UP_OFFSET ){ mAttributes.mAvatarCollisionSphereUpOffset = CLOTH_MAX_AVATAR_COLLISION_SPHERE_UP_OFFSET;} if ( mAttributes.mAvatarCollisionSphereForwardOffset < CLOTH_MIN_AVATAR_COLLISION_SPHERE_FORWARD_OFFSET ){ mAttributes.mAvatarCollisionSphereForwardOffset = CLOTH_MIN_AVATAR_COLLISION_SPHERE_FORWARD_OFFSET;} else if ( mAttributes.mAvatarCollisionSphereForwardOffset > CLOTH_MAX_AVATAR_COLLISION_SPHERE_FORWARD_OFFSET ){ mAttributes.mAvatarCollisionSphereForwardOffset = CLOTH_MAX_AVATAR_COLLISION_SPHERE_FORWARD_OFFSET;} //----------------------------------------------------------------------------------- // based on the above attributes, the strands (flexible objects) are generated //----------------------------------------------------------------------------------- generateStrands(); }//------------------------------------------------------------------ //------------------------------------------------------------------ // set methods //------------------------------------------------------------------ //------------------------------------------------------------------ void LLVOCloth::setNumStrands( int num ) { mAttributes.mNumStrands = num; generateStrands(); }//------------------------------------------------------------------ //------------------------------------------------------------------ void LLVOCloth::setNumSegments( int num ) { mAttributes.mNumSegments = num; generateStrands(); }//------------------------------------------------------------------ //------------------------------------------------------------------ void LLVOCloth::setLength( F32 le ) { mAttributes.mLength = le; generateStrands(); }//------------------------------------------------------------------ //------------------------------------------------------------------ void LLVOCloth::setCircleWrapAmount( F32 n ) { mAttributes.mCircleWrapAmount = n; generateStrands(); }//------------------------------------------------------------------ //------------------------------------------------------------------ void LLVOCloth::setGravity( F32 g ) { mAttributes.mGravity = g; generateStrands(); }//------------------------------------------------------------------ //------------------------------------------------------------------ void LLVOCloth::setPitch( F32 p ) { mAttributes.mPitch = p; generateStrands(); }//------------------------------------------------------------------ //------------------------------------------------------------------ void LLVOCloth::setAirFriction( F32 a ) { mAttributes.mAirFriction = a; generateStrands(); }//------------------------------------------------------------------ //------------------------------------------------------------------ void LLVOCloth::setWindSensitivity( F32 w ) { mAttributes.mWindSensitivity = w; generateStrands(); }//------------------------------------------------------------------ //------------------------------------------------------------------ void LLVOCloth::setTension( F32 t ) { mAttributes.mTension = t; generateStrands(); }//------------------------------------------------------------------ //------------------------------------------------------------------ void LLVOCloth::setWidth( F32 n ) { mAttributes.mWidth = n; generateStrands(); }//------------------------------------------------------------------ //------------------------------------------------------------------ void LLVOCloth::setVisualizingStrands( bool v ) { mAttributes.mVisualizeStrands = v; }//------------------------------------------------------------------//------------------------------------------------------------------ void LLVOCloth::setVisualizingAvCollisionSphere( bool v ) { mAttributes.mVisualizeAvCollisionSphere = v; }//------------------------------------------------------------------ //------------------------------------------------------------------ void LLVOCloth::setAvatarCollisionSphereRadius( F32 r ) { mAttributes.mAvatarCollisionSphereRadius = r; }//------------------------------------------------------------------ //------------------------------------------------------------------ void LLVOCloth::setAvatarCollisionSphereRight( F32 r ) { mAttributes.mAvatarCollisionSphereRightOffset = r; }//------------------------------------------------------------------ //------------------------------------------------------------------ void LLVOCloth::setAvatarCollisionSphereUp( F32 u ) { mAttributes.mAvatarCollisionSphereUpOffset = u; }//------------------------------------------------------------------ //------------------------------------------------------------------ void LLVOCloth::setAvatarCollisionSphereForward( F32 f ) { mAttributes.mAvatarCollisionSphereForwardOffset = f; }//------------------------------------------------------------------ //------------------------------------------------------------------ // get methods //------------------------------------------------------------------ //------------------------------------------------------------------ int LLVOCloth::getNumStrands() { return mAttributes.mNumStrands; }//------------------------------------------------------------------ //------------------------------------------------------------------ int LLVOCloth::getNumSegments() { return mAttributes.mNumSegments; }//------------------------------------------------------------------ //------------------------------------------------------------------ F32 LLVOCloth::getLength() { return mAttributes.mLength; }//------------------------------------------------------------------ //------------------------------------------------------------------ F32 LLVOCloth::getWidth() { return mAttributes.mWidth; }//------------------------------------------------------------------ //------------------------------------------------------------------ F32 LLVOCloth::getCircleWrapAmount() { return mAttributes.mCircleWrapAmount; }//------------------------------------------------------------------ //------------------------------------------------------------------ F32 LLVOCloth::getPitch() { return mAttributes.mPitch; }//------------------------------------------------------------------ //------------------------------------------------------------------ F32 LLVOCloth::getTension() { return mAttributes.mTension; }//------------------------------------------------------------------ //------------------------------------------------------------------ F32 LLVOCloth::getGravity() { return mAttributes.mGravity; }//------------------------------------------------------------------ //------------------------------------------------------------------ F32 LLVOCloth::getAirFriction() { return mAttributes.mAirFriction; }//------------------------------------------------------------------ //------------------------------------------------------------------ F32 LLVOCloth::getWindSensitivity() { return mAttributes.mWindSensitivity; }//------------------------------------------------------------------ //------------------------------------------------------------------ bool LLVOCloth::getVisualizingStrands() const { return mAttributes.mVisualizeStrands; }//------------------------------------------------------------------ //------------------------------------------------------------------ bool LLVOCloth::getVisualizingAvCollisionSphere() const { return mAttributes.mVisualizeAvCollisionSphere; }//------------------------------------------------------------------ //------------------------------------------------------------------ F32 LLVOCloth::getAvatarCollisionSphereRadius() { return mAttributes.mAvatarCollisionSphereRadius; }//------------------------------------------------------------------ //------------------------------------------------------------------ F32 LLVOCloth::getAvatarCollisionSphereRight() { return mAttributes.mAvatarCollisionSphereRightOffset; }//------------------------------------------------------------------ //------------------------------------------------------------------ F32 LLVOCloth::getAvatarCollisionSphereUp() { return mAttributes.mAvatarCollisionSphereUpOffset; }//------------------------------------------------------------------ //------------------------------------------------------------------ F32 LLVOCloth::getAvatarCollisionSphereForward() { return mAttributes.mAvatarCollisionSphereForwardOffset; }//------------------------------------------------------------------ //------------------------------------------------------------------ void LLVOCloth::markAsDead() { mDead = TRUE; }//------------------------------------------------------------------