/** * @file llfloatermodelpreview.h * @brief LLFloaterModelPreview class definition * * $LicenseInfo:firstyear=2004&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$ */ #ifndef LL_LLFLOATERMODELPREVIEW_H #define LL_LLFLOATERMODELPREVIEW_H #include "llfloaternamedesc.h" #include "lldynamictexture.h" #include "llquaternion.h" #include "llmeshrepository.h" #include "llmodel.h" #include "llthread.h" #include "llviewermenufile.h" #include "llfloatermodeluploadbase.h" class LLComboBox; class LLJoint; class LLViewerJointMesh; class LLVOAvatar; class LLTextBox; class LLVertexBuffer; class LLModelPreview; class LLFloaterModelPreview; class daeElement; class domProfile_COMMON; class domInstance_geometry; class domNode; class domTranslate; class domController; class LLMenuButton; class LLToggleableMenu; typedef std::map JointTransformMap; typedef std::map:: iterator JointTransformMapIt; const S32 NUM_LOD = 4; class LLModelLoader : public LLThread { public: typedef enum { STARTING = 0, READING_FILE, CREATING_FACES, GENERATING_VERTEX_BUFFERS, GENERATING_LOD, DONE, ERROR_PARSING, //basically loading failed ERROR_MATERIALS, } eLoadState; U32 mState; std::string mFilename; S32 mLod; LLModelPreview* mPreview; LLMatrix4 mTransform; BOOL mFirstTransform; LLVector3 mExtents[2]; bool mTrySLM; std::map > mModel; typedef std::vector > model_list; model_list mModelList; typedef std::vector model_instance_list; typedef std::map scene; scene mScene; typedef std::queue > model_queue; //queue of models that need a physics rep model_queue mPhysicsQ; LLModelLoader( std::string filename, S32 lod, LLModelPreview* preview, JointTransformMap& jointMap, std::deque& jointsFromNodes ); ~LLModelLoader() ; virtual void run(); bool doLoadModel(); bool loadFromSLM(const std::string& filename); void loadModelCallback(); void loadTextures() ; //called in the main thread. void processElement(daeElement* element, bool& badElement); std::map getMaterials(LLModel* model, domInstance_geometry* instance_geo); LLImportMaterial profileToMaterial(domProfile_COMMON* material); std::string getElementLabel(daeElement *element); LLColor4 getDaeColor(daeElement* element); daeElement* getChildFromElement( daeElement* pElement, std::string const & name ); bool isNodeAJoint( domNode* pNode ); void processJointNode( domNode* pNode, std::map& jointTransforms ); void extractTranslation( domTranslate* pTranslate, LLMatrix4& transform ); void extractTranslationViaElement( daeElement* pTranslateElement, LLMatrix4& transform ); void extractTranslationViaSID( daeElement* pElement, LLMatrix4& transform ); void setLoadState(U32 state); void buildJointToNodeMappingFromScene( daeElement* pRoot ); void processJointToNodeMapping( domNode* pNode ); void processChildJoints( domNode* pParentNode ); //map of avatar joints as named in COLLADA assets to internal joint names std::map mJointMap; JointTransformMap& mJointList; std::deque& mJointsFromNode; S32 mNumOfFetchingTextures ; //updated in the main thread bool areTexturesReady() { return !mNumOfFetchingTextures; } //called in the main thread. private: static std::list sActiveLoaderList; static bool isAlive(LLModelLoader* loader) ; }; class LLFloaterModelPreview : public LLFloaterModelUploadBase { public: class DecompRequest : public LLPhysicsDecomp::Request { public: S32 mContinue; LLPointer mModel; DecompRequest(const std::string& stage, LLModel* mdl); virtual S32 statusCallback(const char* status, S32 p1, S32 p2); virtual void completed(); }; static LLFloaterModelPreview* sInstance; LLFloaterModelPreview(const LLSD& key); virtual ~LLFloaterModelPreview(); virtual BOOL postBuild(); void initModelPreview(); BOOL handleMouseDown(S32 x, S32 y, MASK mask); BOOL handleMouseUp(S32 x, S32 y, MASK mask); BOOL handleHover(S32 x, S32 y, MASK mask); BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); /*virtual*/ void onOpen(const LLSD& key); static void onMouseCaptureLostModelPreview(LLMouseHandler*); static void setUploadAmount(S32 amount) { sUploadAmount = amount; } void setDetails(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost); void onBrowseLOD(S32 lod); static void onReset(void* data); static void onUpload(void* data); void refresh(); void loadModel(S32 lod); void loadModel(S32 lod, const std::string& file_name, bool force_disable_slm = false); void onViewOptionChecked(LLUICtrl* ctrl); bool isViewOptionChecked(const LLSD& userdata); bool isViewOptionEnabled(const LLSD& userdata); void setViewOptionEnabled(const std::string& option, bool enabled); void enableViewOption(const std::string& option); void disableViewOption(const std::string& option); // shows warning message if agent has no permissions to upload model /*virtual*/ void onPermissionsReceived(const LLSD& result); // called when error occurs during permissions request /*virtual*/ void setPermissonsErrorStatus(U32 status, const std::string& reason); /*virtual*/ void onModelPhysicsFeeReceived(const LLSD& result, std::string upload_url); void handleModelPhysicsFeeReceived(); /*virtual*/ void setModelPhysicsFeeErrorStatus(U32 status, const std::string& reason); /*virtual*/ void onModelUploadSuccess(); /*virtual*/ void onModelUploadFailure(); protected: friend class LLModelPreview; friend class LLMeshFilePicker; friend class LLPhysicsDecomp; static void onImportScaleCommit(LLUICtrl*, void*); static void onPelvisOffsetCommit(LLUICtrl*, void*); static void onUploadJointsCommit(LLUICtrl*,void*); static void onUploadSkinCommit(LLUICtrl*,void*); static void onPreviewLODCommit(LLUICtrl*,void*); static void onGenerateNormalsCommit(LLUICtrl*,void*); void toggleGenarateNormals(); static void onAutoFillCommit(LLUICtrl*,void*); void onLODParamCommit(S32 lod, bool enforce_tri_limit); static void onExplodeCommit(LLUICtrl*, void*); static void onPhysicsParamCommit(LLUICtrl* ctrl, void* userdata); static void onPhysicsStageExecute(LLUICtrl* ctrl, void* userdata); static void onCancel(LLUICtrl* ctrl, void* userdata); static void onPhysicsStageCancel(LLUICtrl* ctrl, void* userdata); static void onPhysicsBrowse(LLUICtrl* ctrl, void* userdata); static void onPhysicsUseLOD(LLUICtrl* ctrl, void* userdata); static void onPhysicsOptimize(LLUICtrl* ctrl, void* userdata); static void onPhysicsDecomposeBack(LLUICtrl* ctrl, void* userdata); static void onPhysicsSimplifyBack(LLUICtrl* ctrl, void* userdata); void draw(); void initDecompControls(); void setStatusMessage(const std::string& msg); LLModelPreview* mModelPreview; LLPhysicsDecomp::decomp_params mDecompParams; S32 mLastMouseX; S32 mLastMouseY; LLRect mPreviewRect; static S32 sUploadAmount; std::set > mCurRequest; std::string mStatusMessage; //use "disabled" as false by default std::map mViewOptionDisabled; //store which lod mode each LOD is using // 0 - load from file // 1 - auto generate // 2 - use LoD above S32 mLODMode[4]; LLMutex* mStatusLock; LLSD mModelPhysicsFee; private: void onClickCalculateBtn(); void toggleCalculateButton(); void onLoDSourceCommit(S32 lod); // Toggles between "Calculate weights & fee" and "Upload" buttons. void toggleCalculateButton(bool visible); // resets display options of model preview to their defaults. void resetDisplayOptions(); void createSmoothComboBox(LLComboBox* combo_box, float min, float max); LLButton* mUploadBtn; LLButton* mCalculateBtn; }; class LLMeshFilePicker : public LLFilePickerThread { public: LLMeshFilePicker(LLModelPreview* mp, S32 lod); virtual void notify(const std::string& filename); private: LLModelPreview* mMP; S32 mLOD; }; class LLModelPreview : public LLViewerDynamicTexture, public LLMutex { typedef boost::signals2::signal details_signal_t; typedef boost::signals2::signal model_loaded_signal_t; typedef boost::signals2::signal model_updated_signal_t; public: typedef enum { LOD_FROM_FILE = 0, GENERATE, USE_LOD_ABOVE, } eLoDMode; public: LLModelPreview(S32 width, S32 height, LLFloater* fmp); virtual ~LLModelPreview(); void resetPreviewTarget(); void setPreviewTarget(F32 distance); void setTexture(U32 name) { mTextureName = name; } void setPhysicsFromLOD(S32 lod); BOOL render(); void update(); void genBuffers(S32 lod, bool skinned); void clearBuffers(); void refresh(); void rotate(F32 yaw_radians, F32 pitch_radians); void zoom(F32 zoom_amt); void pan(F32 right, F32 up); virtual BOOL needsRender() { return mNeedsUpdate; } void setPreviewLOD(S32 lod); void clearModel(S32 lod); void loadModel(std::string filename, S32 lod, bool force_disable_slm = false); void loadModelCallback(S32 lod); void genLODs(S32 which_lod = -1, U32 decimation = 3, bool enforce_tri_limit = false); void generateNormals(); U32 calcResourceCost(); void rebuildUploadData(); void saveUploadData(bool save_skinweights, bool save_joint_poisitions); void saveUploadData(const std::string& filename, bool save_skinweights, bool save_joint_poisitions); void clearIncompatible(S32 lod); void updateStatusMessages(); void updateLodControls(S32 lod); void clearGLODGroup(); void onLODParamCommit(S32 lod, bool enforce_tri_limit); void addEmptyFace( LLModel* pTarget ); const bool getModelPivot( void ) const { return mHasPivot; } void setHasPivot( bool val ) { mHasPivot = val; } void setModelPivot( const LLVector3& pivot ) { mModelPivot = pivot; } //Determines the viability of an asset to be used as an avatar rig (w or w/o joint upload caps) void critiqueRigForUploadApplicability( const std::vector &jointListFromAsset ); void critiqueJointToNodeMappingFromScene( void ); //Is a rig valid so that it can be used as a criteria for allowing for uploading of joint positions //Accessors for joint position upload friendly rigs const bool isRigValidForJointPositionUpload( void ) const { return mRigValidJointUpload; } void setRigValidForJointPositionUpload( bool rigValid ) { mRigValidJointUpload = rigValid; } bool isRigSuitableForJointPositionUpload( const std::vector &jointListFromAsset ); //Determines if a rig is a legacy from the joint list bool isRigLegacy( const std::vector &jointListFromAsset ); //Accessors for the legacy rigs const bool isLegacyRigValid( void ) const { return mLegacyRigValid; } void setLegacyRigValid( bool rigValid ) { mLegacyRigValid = rigValid; } //Verify that a controller matches vertex counts bool verifyController( domController* pController ); static void textureLoadedCallback( BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* userdata ); boost::signals2::connection setDetailsCallback( const details_signal_t::slot_type& cb ){ return mDetailsSignal.connect(cb); } boost::signals2::connection setModelLoadedCallback( const model_loaded_signal_t::slot_type& cb ){ return mModelLoadedSignal.connect(cb); } boost::signals2::connection setModelUpdatedCallback( const model_updated_signal_t::slot_type& cb ){ return mModelUpdatedSignal.connect(cb); } void setLoadState( U32 state ) { mLoadState = state; } U32 getLoadState() { return mLoadState; } void setRigWithSceneParity( bool state ) { mRigParityWithScene = state; } const bool getRigWithSceneParity( void ) const { return mRigParityWithScene; } LLVector3 getTranslationForJointOffset( std::string joint ); private: //Utility function for controller vertex compare bool verifyCount( int expected, int result ); //Creates the dummy avatar for the preview window void createPreviewAvatar( void ); //Accessor for the dummy avatar LLVOAvatar* getPreviewAvatar( void ) { return mPreviewAvatar; } protected: friend class LLModelLoader; friend class LLFloaterModelPreview; friend class LLFloaterModelPreview::DecompRequest; friend class LLPhysicsDecomp; LLFloater* mFMP; BOOL mNeedsUpdate; bool mDirty; bool mGenLOD; U32 mTextureName; F32 mCameraDistance; F32 mCameraYaw; F32 mCameraPitch; F32 mCameraZoom; LLVector3 mCameraOffset; LLVector3 mPreviewTarget; LLVector3 mPreviewScale; S32 mPreviewLOD; U32 mResourceCost; std::string mLODFile[LLModel::NUM_LODS]; bool mLoading; U32 mLoadState; bool mResetJoints; bool mRigParityWithScene; std::map mViewOption; //GLOD object parameters (must rebuild object if these change) bool mLODFrozen; F32 mBuildShareTolerance; U32 mBuildQueueMode; U32 mBuildOperator; U32 mBuildBorderMode; U32 mRequestedLoDMode[LLModel::NUM_LODS]; S32 mRequestedTriangleCount[LLModel::NUM_LODS]; F32 mRequestedErrorThreshold[LLModel::NUM_LODS]; U32 mRequestedBuildOperator[LLModel::NUM_LODS]; U32 mRequestedQueueMode[LLModel::NUM_LODS]; U32 mRequestedBorderMode[LLModel::NUM_LODS]; F32 mRequestedShareTolerance[LLModel::NUM_LODS]; F32 mRequestedCreaseAngle[LLModel::NUM_LODS]; LLModelLoader* mModelLoader; LLModelLoader::scene mScene[LLModel::NUM_LODS]; LLModelLoader::scene mBaseScene; LLModelLoader::model_list mModel[LLModel::NUM_LODS]; LLModelLoader::model_list mBaseModel; U32 mGroup; std::map, U32> mObject; U32 mMaxTriangleLimit; LLMeshUploadThread::instance_list mUploadData; std::set mTextureSet; //map of vertex buffers to models (one vertex buffer in vector per face in model std::map > > mVertexBuffer[LLModel::NUM_LODS+1]; details_signal_t mDetailsSignal; model_loaded_signal_t mModelLoadedSignal; model_updated_signal_t mModelUpdatedSignal; LLVector3 mModelPivot; bool mHasPivot; float mPelvisZOffset; bool mRigValidJointUpload; bool mLegacyRigValid; bool mLastJointUpdate; std::deque mMasterJointList; std::deque mMasterLegacyJointList; std::deque mJointsFromNode; JointTransformMap mJointTransformMap; LLPointer mPreviewAvatar; }; #endif // LL_LLFLOATERMODELPREVIEW_H