diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index fe5a4c03d..f3cc6d444 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -185,6 +185,7 @@ set(viewer_SOURCE_FILES llfloaterbuycontents.cpp llfloaterbuycurrency.cpp llfloaterbuyland.cpp + llfloaterbvhpreview.cpp llfloatercamera.cpp llfloaterchat.cpp llfloaterchatterbox.cpp @@ -688,6 +689,7 @@ set(viewer_HEADER_FILES llfloaterbuycontents.h llfloaterbuycurrency.h llfloaterbuyland.h + llfloaterbvhpreview.h llfloatercamera.h llfloaterchat.h llfloaterchatterbox.h diff --git a/indra/newview/llfloateranimpreview.cpp b/indra/newview/llfloateranimpreview.cpp index 6aa573ba8..8fdfc1a93 100644 --- a/indra/newview/llfloateranimpreview.cpp +++ b/indra/newview/llfloateranimpreview.cpp @@ -2,9 +2,7 @@ * @file llfloateranimpreview.cpp * @brief LLFloaterAnimPreview class implementation * - * $LicenseInfo:firstyear=2004&license=viewergpl$ - * - * Copyright (c) 2004-2009, Linden Research, Inc. + * Copyright (c) 2012, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab @@ -31,1600 +29,18 @@ */ #include "llviewerprecompiledheaders.h" - #include "llfloateranimpreview.h" -#include "llbvhloader.h" -#include "lldatapacker.h" -#include "lldir.h" -#include "llnotificationsutil.h" -#include "llvfile.h" -#include "llapr.h" -#include "llstring.h" - -#include "llagent.h" -#include "llanimationstates.h" -#include "llbbox.h" -#include "llbutton.h" -#include "llcheckboxctrl.h" -#include "llcombobox.h" -#include "lldrawable.h" -#include "lldrawpoolavatar.h" -#include "llrender.h" -#include "llface.h" -#include "llfocusmgr.h" -#include "llkeyframemotion.h" -#include "lllineeditor.h" -#include "llfloaterperms.h" -#include "llsliderctrl.h" -#include "llspinctrl.h" -#include "lltextbox.h" -#include "lltoolmgr.h" -#include "llui.h" -#include "llviewercamera.h" -#include "llviewerobjectlist.h" -#include "llviewerwindow.h" -#include "llviewermenufile.h" // upload_new_resource() -#include "llvoavatarself.h" -#include "pipeline.h" -#include "lluictrlfactory.h" -#include "llviewercontrol.h" - -#include "hippogridmanager.h" - -// -#include "llinventorymodel.h" // gInventoryModel -// - -S32 LLFloaterAnimPreview::sUploadAmount = 10; - -const S32 PREVIEW_BORDER_WIDTH = 2; -const S32 PREVIEW_RESIZE_HANDLE_SIZE = S32(RESIZE_HANDLE_WIDTH * OO_SQRT2) + PREVIEW_BORDER_WIDTH; -const S32 PREVIEW_HPAD = PREVIEW_RESIZE_HANDLE_SIZE; -const S32 PREF_BUTTON_HEIGHT = 16; -const S32 PREVIEW_TEXTURE_HEIGHT = 300; - -const F32 PREVIEW_CAMERA_DISTANCE = 4.f; - -const F32 MIN_CAMERA_ZOOM = 0.5f; -const F32 MAX_CAMERA_ZOOM = 10.f; - -const F32 BASE_ANIM_TIME_OFFSET = 5.f; - -// -struct LLSaveInfo +LLFloaterAnimPreview::LLFloaterAnimPreview(LLSD const& filename) : LLFloaterNameDesc(filename) { - LLSaveInfo(const LLUUID& item_id, const LLUUID& object_id, const std::string& desc, - const LLTransactionID tid) - : mItemUUID(item_id), mObjectUUID(object_id), mDesc(desc), mTransactionID(tid) - { - } - - LLUUID mItemUUID; - LLUUID mObjectUUID; - std::string mDesc; - LLTransactionID mTransactionID; -}; -// - -std::string STATUS[] = -{ - "E_ST_OK", - "E_ST_EOF", - "E_ST_NO_CONSTRAINT", - "E_ST_NO_FILE", - "E_ST_NO_HIER", - "E_ST_NO_JOINT", - "E_ST_NO_NAME", - "E_ST_NO_OFFSET", - "E_ST_NO_CHANNELS", - "E_ST_NO_ROTATION", - "E_ST_NO_AXIS", - "E_ST_NO_MOTION", - "E_ST_NO_FRAMES", - "E_ST_NO_FRAME_TIME", - "E_ST_NO_POS", - "E_ST_NO_ROT", - "E_ST_NO_XLT_FILE", - "E_ST_NO_XLT_HEADER", - "E_ST_NO_XLT_NAME", - "E_ST_NO_XLT_IGNORE", - "E_ST_NO_XLT_RELATIVE", - "E_ST_NO_XLT_OUTNAME", - "E_ST_NO_XLT_MATRIX", - "E_ST_NO_XLT_MERGECHILD", - "E_ST_NO_XLT_MERGEPARENT", - "E_ST_NO_XLT_PRIORITY", - "E_ST_NO_XLT_LOOP", - "E_ST_NO_XLT_EASEIN", - "E_ST_NO_XLT_EASEOUT", - "E_ST_NO_XLT_HAND", - "E_ST_NO_XLT_EMOTE", -"E_ST_BAD_ROOT" -}; - - -//----------------------------------------------------------------------------- -// LLFloaterAnimPreview() -//----------------------------------------------------------------------------- -LLFloaterAnimPreview::LLFloaterAnimPreview(const std::string& filename, void* item) : - LLFloaterNameDesc(filename) -{ - // - mItem = item; - // - - mLastMouseX = 0; - mLastMouseY = 0; - - mIDList["Standing"] = ANIM_AGENT_STAND; - mIDList["Walking"] = ANIM_AGENT_FEMALE_WALK; - mIDList["Sitting"] = ANIM_AGENT_SIT_FEMALE; - mIDList["Flying"] = ANIM_AGENT_HOVER; - - mIDList["[None]"] = LLUUID::null; - mIDList["Aaaaah"] = ANIM_AGENT_EXPRESS_OPEN_MOUTH; - mIDList["Afraid"] = ANIM_AGENT_EXPRESS_AFRAID; - mIDList["Angry"] = ANIM_AGENT_EXPRESS_ANGER; - mIDList["Big Smile"] = ANIM_AGENT_EXPRESS_TOOTHSMILE; - mIDList["Bored"] = ANIM_AGENT_EXPRESS_BORED; - mIDList["Cry"] = ANIM_AGENT_EXPRESS_CRY; - mIDList["Disdain"] = ANIM_AGENT_EXPRESS_DISDAIN; - mIDList["Embarrassed"] = ANIM_AGENT_EXPRESS_EMBARRASSED; - mIDList["Frown"] = ANIM_AGENT_EXPRESS_FROWN; - mIDList["Kiss"] = ANIM_AGENT_EXPRESS_KISS; - mIDList["Laugh"] = ANIM_AGENT_EXPRESS_LAUGH; - mIDList["Plllppt"] = ANIM_AGENT_EXPRESS_TONGUE_OUT; - mIDList["Repulsed"] = ANIM_AGENT_EXPRESS_REPULSED; - mIDList["Sad"] = ANIM_AGENT_EXPRESS_SAD; - mIDList["Shrug"] = ANIM_AGENT_EXPRESS_SHRUG; - mIDList["Smile"] = ANIM_AGENT_EXPRESS_SMILE; - mIDList["Surprise"] = ANIM_AGENT_EXPRESS_SURPRISE; - mIDList["Wink"] = ANIM_AGENT_EXPRESS_WINK; - mIDList["Worry"] = ANIM_AGENT_EXPRESS_WORRY; } -//----------------------------------------------------------------------------- -// setAnimCallbacks() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::setAnimCallbacks() -{ - childSetCommitCallback("playback_slider", onSliderMove, this); - - childSetCommitCallback("preview_base_anim", onCommitBaseAnim, this); - childSetValue("preview_base_anim", "Standing"); - - childSetCommitCallback("priority", onCommitPriority, this); - childSetCommitCallback("loop_check", onCommitLoop, this); - childSetCommitCallback("loop_in_point", onCommitLoopIn, this); - childSetValidate("loop_in_point", validateLoopIn); - childSetCommitCallback("loop_out_point", onCommitLoopOut, this); - childSetValidate("loop_out_point", validateLoopOut); - - childSetCommitCallback("hand_pose_combo", onCommitHandPose, this); - - childSetCommitCallback("emote_combo", onCommitEmote, this); - childSetValue("emote_combo", "[None]"); - - childSetCommitCallback("ease_in_time", onCommitEaseIn, this); - childSetValidate("ease_in_time", validateEaseIn); - childSetCommitCallback("ease_out_time", onCommitEaseOut, this); - childSetValidate("ease_out_time", validateEaseOut); -} - -//----------------------------------------------------------------------------- -// postBuild() -//----------------------------------------------------------------------------- BOOL LLFloaterAnimPreview::postBuild() { - LLRect r; - LLKeyframeMotion* motionp = NULL; - LLBVHLoader* loaderp = NULL; - if (!LLFloaterNameDesc::postBuild()) { return FALSE; } - - mInWorld = gSavedSettings.getBOOL("PreviewAnimInWorld"); - - childSetCommitCallback("name_form", onCommitName, this); - - if (gSavedSettings.getBOOL("AscentPowerfulWizard")) - { - childSetMaxValue("priority", 7); - } - - childSetLabelArg("ok_btn", "[UPLOADFEE]", gHippoGridManager->getConnectedGrid()->getUploadFee()); - childSetAction("ok_btn", onBtnOK, this); - setDefaultBtn(); - - if (mInWorld) - { - r = getRect(); - translate(0, 230); - reshape(r.getWidth(), r.getHeight() - 230); - childSetValue("bad_animation_text", getString("in_world")); - childShow("bad_animation_text"); - } - else - { - childHide("bad_animation_text"); - } - - mPreviewRect.set(PREVIEW_HPAD, - PREVIEW_TEXTURE_HEIGHT, - getRect().getWidth() - PREVIEW_HPAD, - PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD); - mPreviewImageRect.set(0.f, 1.f, 1.f, 0.f); - - S32 y = mPreviewRect.mTop + BTN_HEIGHT; - S32 btn_left = PREVIEW_HPAD; - - r.set( btn_left, y, btn_left + 32, y - BTN_HEIGHT ); - mPlayButton = getChild( "play_btn"); - if (!mPlayButton) - { - mPlayButton = new LLButton(std::string("play_btn"), LLRect(0,0,0,0)); - } - mPlayButton->setClickedCallback(boost::bind(&LLFloaterAnimPreview::onBtnPlay,this)); - - mPlayButton->setImages(std::string("button_anim_play.tga"), - std::string("button_anim_play_selected.tga")); - - mPlayButton->setImageDisabled(NULL); - mPlayButton->setImageDisabledSelected(NULL); - - mPlayButton->setScaleImage(TRUE); - - mStopButton = getChild( "stop_btn"); - if (!mStopButton) - { - mStopButton = new LLButton(std::string("stop_btn"), LLRect(0,0,0,0)); - } - mStopButton->setClickedCallback(boost::bind(&LLFloaterAnimPreview::onBtnStop, this)); - - mStopButton->setImages(std::string("button_anim_stop.tga"), - std::string("button_anim_stop_selected.tga")); - - mStopButton->setImageDisabled(NULL); - mStopButton->setImageDisabledSelected(NULL); - - mStopButton->setScaleImage(TRUE); - - r.set(r.mRight + PREVIEW_HPAD, y, getRect().getWidth() - PREVIEW_HPAD, y - BTN_HEIGHT); - //childSetCommitCallback("playback_slider", onSliderMove, this); - - //childSetCommitCallback("preview_base_anim", onCommitBaseAnim, this); - //childSetValue("preview_base_anim", "Standing"); - - //childSetCommitCallback("priority", onCommitPriority, this); - //childSetCommitCallback("loop_check", onCommitLoop, this); - //childSetCommitCallback("loop_in_point", onCommitLoopIn, this); - //childSetValidate("loop_in_point", validateLoopIn); - //childSetCommitCallback("loop_out_point", onCommitLoopOut, this); - //childSetValidate("loop_out_point", validateLoopOut); - - //childSetCommitCallback("hand_pose_combo", onCommitHandPose, this); - - //childSetCommitCallback("emote_combo", onCommitEmote, this); - //childSetValue("emote_combo", "[None]"); - - //childSetCommitCallback("ease_in_time", onCommitEaseIn, this); - //childSetValidate("ease_in_time", validateEaseIn); - //childSetCommitCallback("ease_out_time", onCommitEaseOut, this); - //childSetValidate("ease_out_time", validateEaseOut); - - // moved declaration from below - BOOL success = false; - // - - std::string exten = gDirUtilp->getExtension(mFilename); - if (exten == "bvh") - { - // loading a bvh file - - // now load bvh file - S32 file_size; - - LLAPRFile infile(mFilenameAndPath, LL_APR_RB, &file_size); - - if (!infile.getFileHandle()) - { - llwarns << "Can't open BVH file:" << mFilename << llendl; - } - else - { - char* file_buffer; - - file_buffer = new char[file_size + 1]; - - if (file_size == infile.read(file_buffer, file_size)) - { - file_buffer[file_size] = '\0'; - llinfos << "Loading BVH file " << mFilename << llendl; - ELoadStatus load_status = E_ST_OK; - S32 line_number = 0; - loaderp = new LLBVHLoader(file_buffer, load_status, line_number); - std::string status = getString(STATUS[load_status]); - - if(load_status == E_ST_NO_XLT_FILE) - { - llwarns << "NOTE: No translation table found." << llendl; - } - else - { - llwarns << "ERROR: [line: " << line_number << "] " << status << llendl; - } - } - - infile.close() ; - delete[] file_buffer; - - // moved everything bvh from below - if(loaderp && loaderp->isInitialized() && loaderp->getDuration() <= MAX_ANIM_DURATION) - { - mTransactionID.generate(); - mMotionID = mTransactionID.makeAssetID(gAgent.getSecureSessionID()); - - mAnimPreview = new LLPreviewAnimation(256, 256); - - // motion will be returned, but it will be in a load-pending state, as this is a new motion - // this motion will not request an asset transfer until next update, so we have a chance to - // load the keyframe data locally - if (mInWorld) - { - motionp = (LLKeyframeMotion*)gAgentAvatarp->createMotion(mMotionID); - } - else - { - motionp = (LLKeyframeMotion*)mAnimPreview->getDummyAvatar()->createMotion(mMotionID); - } - - // create data buffer for keyframe initialization - S32 buffer_size = loaderp->getOutputSize(); - U8* buffer = new U8[buffer_size]; - - LLDataPackerBinaryBuffer dp(buffer, buffer_size); - - // pass animation data through memory buffer - loaderp->serialize(dp); - dp.reset(); - success = motionp && motionp->deserialize(dp); - } - else - { - success = false; - if ( loaderp ) - { - if (loaderp->getDuration() > MAX_ANIM_DURATION) - { - LLUIString out_str = getString("anim_too_long"); - out_str.setArg("[LENGTH]", llformat("%.1f", loaderp->getDuration())); - out_str.setArg("[MAX_LENGTH]", llformat("%.1f", MAX_ANIM_DURATION)); - getChild("bad_animation_text")->setValue(out_str.getString()); - } - else - { - LLUIString out_str = getString("failed_file_read"); - out_str.setArg("[STATUS]", getString(STATUS[loaderp->getStatus()])); - getChild("bad_animation_text")->setValue(out_str.getString()); - } - } - - //setEnabled(FALSE); - mMotionID.setNull(); - mAnimPreview = NULL; - } - // - } - } - // - else if(exten == "animatn") - { - S32 file_size; - LLAPRFile raw_animatn(mFilenameAndPath, LL_APR_RB, &file_size); - - if (!raw_animatn.getFileHandle()) - { - llwarns << "Can't open animatn file:" << mFilename << llendl; - } - else - { - char* file_buffer; - - file_buffer = new char[file_size + 1]; - - if (file_size == raw_animatn.read(file_buffer, file_size)) - { - file_buffer[file_size] = '\0'; - llinfos << "Loading animatn file " << mFilename << llendl; - mTransactionID.generate(); - mMotionID = mTransactionID.makeAssetID(gAgent.getSecureSessionID()); - mAnimPreview = new LLPreviewAnimation(256, 256); - motionp = (LLKeyframeMotion*)mAnimPreview->getDummyAvatar()->createMotion(mMotionID); - LLDataPackerBinaryBuffer dp((U8*)file_buffer, file_size); - dp.reset(); - success = motionp && motionp->deserialize(dp); - } - - raw_animatn.close(); - delete[] file_buffer; - } - } - // - - if (success) - { - setAnimCallbacks() ; - - if (!mInWorld) - { - const LLBBoxLocal &pelvis_bbox = motionp->getPelvisBBox(); - - LLVector3 temp = pelvis_bbox.getCenter(); - // only consider XY? - //temp.mV[VZ] = 0.f; - F32 pelvis_offset = temp.magVec(); - - temp = pelvis_bbox.getExtent(); - //temp.mV[VZ] = 0.f; - F32 pelvis_max_displacement = pelvis_offset + (temp.magVec() * 0.5f) + 1.f; - - F32 camera_zoom = LLViewerCamera::getInstance()->getDefaultFOV() / (2.f * atan(pelvis_max_displacement / PREVIEW_CAMERA_DISTANCE)); - - mAnimPreview->setZoom(camera_zoom); - } - - motionp->setName(childGetValue("name_form").asString()); - if (!mInWorld) - { - mAnimPreview->getDummyAvatar()->startMotion(mMotionID); - } - childSetMinValue("playback_slider", 0.0); - childSetMaxValue("playback_slider", 1.0); - - childSetValue("loop_check", LLSD(motionp->getLoop())); - childSetValue("loop_in_point", LLSD(motionp->getLoopIn() / motionp->getDuration() * 100.f)); - childSetValue("loop_out_point", LLSD(motionp->getLoopOut() / motionp->getDuration() * 100.f)); - childSetValue("priority", LLSD((F32)motionp->getPriority())); - childSetValue("hand_pose_combo", LLHandMotion::getHandPoseName(motionp->getHandPose())); - childSetValue("ease_in_time", LLSD(motionp->getEaseInDuration())); - childSetValue("ease_out_time", LLSD(motionp->getEaseOutDuration())); - setEnabled(TRUE); - std::string seconds_string; - seconds_string = llformat(" - %.2f seconds", motionp->getDuration()); - - setTitle(mFilename + std::string(seconds_string)); - } - else - { - mAnimPreview = NULL; - mMotionID.setNull(); - childSetValue("bad_animation_text", getString("failed_to_initialize")); - } - - - refresh(); - - delete loaderp; - + getChild("ok_btn")->setCommitCallback(boost::bind(&LLFloaterNameDesc::onBtnOK, this)); return TRUE; } - -//----------------------------------------------------------------------------- -// LLFloaterAnimPreview() -//----------------------------------------------------------------------------- -LLFloaterAnimPreview::~LLFloaterAnimPreview() -{ - if (mInWorld) - { - LLVOAvatar* avatarp = gAgentAvatarp; - if (avatarp) - { - if (mMotionID.notNull()) - { - avatarp->stopMotion(mMotionID, TRUE); - avatarp->removeMotion(mMotionID); - } - avatarp->deactivateAllMotions(); - avatarp->startMotion(ANIM_AGENT_HEAD_ROT); - avatarp->startMotion(ANIM_AGENT_EYE); - avatarp->startMotion(ANIM_AGENT_BODY_NOISE); - avatarp->startMotion(ANIM_AGENT_BREATHE_ROT); - avatarp->startMotion(ANIM_AGENT_HAND_MOTION); - avatarp->startMotion(ANIM_AGENT_PELVIS_FIX); - avatarp->startMotion(ANIM_AGENT_STAND, BASE_ANIM_TIME_OFFSET); - } - } - mAnimPreview = NULL; - - setEnabled(FALSE); -} - -//----------------------------------------------------------------------------- -// draw() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::draw() -{ - LLFloater::draw(); - LLRect r = getRect(); - - refresh(); - - if (mMotionID.notNull() && mAnimPreview && !mInWorld) - { - gGL.color3f(1.f, 1.f, 1.f); - - gGL.getTexUnit(0)->bind(mAnimPreview); - - gGL.begin( LLRender::QUADS ); - { - gGL.texCoord2f(0.f, 1.f); - gGL.vertex2i(PREVIEW_HPAD, PREVIEW_TEXTURE_HEIGHT); - gGL.texCoord2f(0.f, 0.f); - gGL.vertex2i(PREVIEW_HPAD, PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD); - gGL.texCoord2f(1.f, 0.f); - gGL.vertex2i(r.getWidth() - PREVIEW_HPAD, PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD); - gGL.texCoord2f(1.f, 1.f); - gGL.vertex2i(r.getWidth() - PREVIEW_HPAD, PREVIEW_TEXTURE_HEIGHT); - } - gGL.end(); - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - LLVOAvatar* avatarp = mAnimPreview->getDummyAvatar(); - if (!avatarp->areAnimationsPaused()) - { - mAnimPreview->requestUpdate(); - } - } -} - -//----------------------------------------------------------------------------- -// resetMotion() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::resetMotion() -{ - LLVOAvatar* avatarp; - if (mInWorld) - { - avatarp = gAgentAvatarp; - } - else - { - avatarp = mAnimPreview->getDummyAvatar(); - } - if (!avatarp) - { - return; - } - - BOOL paused = avatarp->areAnimationsPaused(); - - // *TODO: Fix awful casting hack - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(mMotionID); - - // Set emotion - std::string emote = childGetValue("emote_combo").asString(); - motionp->setEmote(mIDList[emote]); - - LLUUID base_id = mIDList[childGetValue("preview_base_anim").asString()]; - avatarp->deactivateAllMotions(); - avatarp->startMotion(base_id, BASE_ANIM_TIME_OFFSET); - avatarp->startMotion(mMotionID, 0.0f); - childSetValue("playback_slider", 0.0f); - - // Set pose - std::string handpose = childGetValue("hand_pose_combo").asString(); - avatarp->startMotion( ANIM_AGENT_HAND_MOTION, 0.0f ); - motionp->setHandPose(LLHandMotion::getHandPose(handpose)); - - if (paused) - { - mPauseRequest = avatarp->requestPause(); - } - else - { - mPauseRequest = NULL; - } -} - -//----------------------------------------------------------------------------- -// handleMouseDown() -//----------------------------------------------------------------------------- -BOOL LLFloaterAnimPreview::handleMouseDown(S32 x, S32 y, MASK mask) -{ - if (!mInWorld && mPreviewRect.pointInRect(x, y)) - { - bringToFront( x, y ); - gFocusMgr.setMouseCapture(this); - gViewerWindow->hideCursor(); - mLastMouseX = x; - mLastMouseY = y; - return TRUE; - } - - return LLFloater::handleMouseDown(x, y, mask); -} - -//----------------------------------------------------------------------------- -// handleMouseUp() -//----------------------------------------------------------------------------- -BOOL LLFloaterAnimPreview::handleMouseUp(S32 x, S32 y, MASK mask) -{ - if (!mInWorld) - { - gFocusMgr.setMouseCapture(FALSE); - gViewerWindow->showCursor(); - } - return LLFloater::handleMouseUp(x, y, mask); -} - -//----------------------------------------------------------------------------- -// handleHover() -//----------------------------------------------------------------------------- -BOOL LLFloaterAnimPreview::handleHover(S32 x, S32 y, MASK mask) -{ - if (mInWorld) - { - return TRUE; - } - - MASK local_mask = mask & ~MASK_ALT; - - if (mAnimPreview && hasMouseCapture()) - { - if (local_mask == MASK_PAN) - { - // pan here - mAnimPreview->pan((F32)(x - mLastMouseX) * -0.005f, (F32)(y - mLastMouseY) * -0.005f); - } - else if (local_mask == MASK_ORBIT) - { - F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f; - F32 pitch_radians = (F32)(y - mLastMouseY) * 0.02f; - - mAnimPreview->rotate(yaw_radians, pitch_radians); - } - else - { - F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f; - F32 zoom_amt = (F32)(y - mLastMouseY) * 0.02f; - - mAnimPreview->rotate(yaw_radians, 0.f); - mAnimPreview->zoom(zoom_amt); - } - - mAnimPreview->requestUpdate(); - - LLUI::setMousePositionLocal(this, mLastMouseX, mLastMouseY); - } - - if (!mPreviewRect.pointInRect(x, y) || !mAnimPreview) - { - return LLFloater::handleHover(x, y, mask); - } - else if (local_mask == MASK_ORBIT) - { - gViewerWindow->setCursor(UI_CURSOR_TOOLCAMERA); - } - else if (local_mask == MASK_PAN) - { - gViewerWindow->setCursor(UI_CURSOR_TOOLPAN); - } - else - { - gViewerWindow->setCursor(UI_CURSOR_TOOLZOOMIN); - } - - return TRUE; -} - -//----------------------------------------------------------------------------- -// handleScrollWheel() -//----------------------------------------------------------------------------- -BOOL LLFloaterAnimPreview::handleScrollWheel(S32 x, S32 y, S32 clicks) -{ - if (!mInWorld) - { - mAnimPreview->zoom((F32)clicks * -0.2f); - mAnimPreview->requestUpdate(); - } - return TRUE; -} - -//----------------------------------------------------------------------------- -// onMouseCaptureLost() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onMouseCaptureLost() -{ - if (!mInWorld) - { - gViewerWindow->showCursor(); - } -} - -//----------------------------------------------------------------------------- -// onBtnPlay() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onBtnPlay(void* user_data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)user_data; - if (!previewp->getEnabled()) - return; - - if (previewp->mMotionID.notNull()) - { - LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } - avatarp = gAgentAvatarp; - } - else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - - if(!avatarp->isMotionActive(previewp->mMotionID)) - { - previewp->resetMotion(); - previewp->mPauseRequest = NULL; - } - else - { - if (avatarp->areAnimationsPaused()) - { - previewp->mPauseRequest = NULL; - } - else - { - previewp->mPauseRequest = avatarp->requestPause(); - } - } - } -} - -//----------------------------------------------------------------------------- -// onBtnStop() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onBtnStop(void* user_data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)user_data; - if (!previewp->getEnabled()) - return; - - if (previewp->mMotionID.notNull()) - { - LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } - avatarp = gAgentAvatarp; - } - else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - previewp->resetMotion(); - previewp->mPauseRequest = avatarp->requestPause(); - } -} - -//----------------------------------------------------------------------------- -// onSliderMove() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onSliderMove(LLUICtrl* ctrl, void*user_data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)user_data; - if (!previewp->getEnabled()) - return; - - LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } - avatarp = gAgentAvatarp; - } - else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - F32 slider_value = (F32)previewp->childGetValue("playback_slider").asReal(); - LLUUID base_id = previewp->mIDList[previewp->childGetValue("preview_base_anim").asString()]; - LLMotion* motionp = avatarp->findMotion(previewp->mMotionID); - F32 duration = motionp->getDuration();// + motionp->getEaseOutDuration(); - F32 delta_time = duration * slider_value; - avatarp->deactivateAllMotions(); - avatarp->startMotion(base_id, delta_time + BASE_ANIM_TIME_OFFSET); - avatarp->startMotion(previewp->mMotionID, delta_time); - previewp->mPauseRequest = avatarp->requestPause(); - previewp->refresh(); -} - -//----------------------------------------------------------------------------- -// onCommitBaseAnim() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onCommitBaseAnim(LLUICtrl* ctrl, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - if (!previewp->getEnabled()) - return; - - LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } - avatarp = gAgentAvatarp; - } - else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - - BOOL paused = avatarp->areAnimationsPaused(); - - // stop all other possible base motions - avatarp->stopMotion(ANIM_AGENT_STAND, TRUE); - avatarp->stopMotion(ANIM_AGENT_WALK, TRUE); - avatarp->stopMotion(ANIM_AGENT_SIT, TRUE); - avatarp->stopMotion(ANIM_AGENT_HOVER, TRUE); - - previewp->resetMotion(); - - if (!paused) - { - previewp->mPauseRequest = NULL; - } -} - -//----------------------------------------------------------------------------- -// onCommitLoop() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onCommitLoop(LLUICtrl* ctrl, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - if (!previewp->getEnabled()) - return; - - LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } - avatarp = gAgentAvatarp; - } - else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); - - if (motionp) - { - motionp->setLoop(previewp->childGetValue("loop_check").asBoolean()); - motionp->setLoopIn((F32)previewp->childGetValue("loop_in_point").asReal() * 0.01f * motionp->getDuration()); - motionp->setLoopOut((F32)previewp->childGetValue("loop_out_point").asReal() * 0.01f * motionp->getDuration()); - } -} - -//----------------------------------------------------------------------------- -// onCommitLoopIn() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onCommitLoopIn(LLUICtrl* ctrl, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - if (!previewp->getEnabled()) - return; - - LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } - avatarp = gAgentAvatarp; - } - else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); - - if (motionp) - { - motionp->setLoopIn((F32)previewp->childGetValue("loop_in_point").asReal() / 100.f); - previewp->resetMotion(); - previewp->childSetValue("loop_check", LLSD(TRUE)); - onCommitLoop(ctrl, data); - } -} - -//----------------------------------------------------------------------------- -// onCommitLoopOut() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onCommitLoopOut(LLUICtrl* ctrl, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - if (!previewp->getEnabled()) - return; - - LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } - avatarp = gAgentAvatarp; - } - else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); - - if (motionp) - { - motionp->setLoopOut((F32)previewp->childGetValue("loop_out_point").asReal() * 0.01f * motionp->getDuration()); - previewp->resetMotion(); - previewp->childSetValue("loop_check", LLSD(TRUE)); - onCommitLoop(ctrl, data); - } -} - -//----------------------------------------------------------------------------- -// onCommitName() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onCommitName(LLUICtrl* ctrl, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - if (!previewp->getEnabled()) - return; - - LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } - avatarp = gAgentAvatarp; - } - else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); - - if (motionp) - { - motionp->setName(previewp->childGetValue("name_form").asString()); - } - - LLFloaterNameDesc::doCommit(ctrl, data); -} - -//----------------------------------------------------------------------------- -// onCommitHandPose() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onCommitHandPose(LLUICtrl* ctrl, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - if (!previewp->getEnabled()) - return; - - previewp->resetMotion(); // sets hand pose -} - -//----------------------------------------------------------------------------- -// onCommitEmote() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onCommitEmote(LLUICtrl* ctrl, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - if (!previewp->getEnabled()) - return; - - previewp->resetMotion(); // ssts emote -} - -//----------------------------------------------------------------------------- -// onCommitPriority() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onCommitPriority(LLUICtrl* ctrl, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - if (!previewp->getEnabled()) - return; - - LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } - avatarp = gAgentAvatarp; - } - else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); - - motionp->setPriority(llfloor((F32)previewp->childGetValue("priority").asReal())); -} - -//----------------------------------------------------------------------------- -// onCommitEaseIn() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onCommitEaseIn(LLUICtrl* ctrl, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - if (!previewp->getEnabled()) - return; - - LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } - avatarp = gAgentAvatarp; - } - else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); - - motionp->setEaseIn((F32)previewp->childGetValue("ease_in_time").asReal()); - previewp->resetMotion(); -} - -//----------------------------------------------------------------------------- -// onCommitEaseOut() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onCommitEaseOut(LLUICtrl* ctrl, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - if (!previewp->getEnabled()) - return; - - LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } - avatarp = gAgentAvatarp; - } - else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); - - motionp->setEaseOut((F32)previewp->childGetValue("ease_out_time").asReal()); - previewp->resetMotion(); -} - -//----------------------------------------------------------------------------- -// validateEaseIn() -//----------------------------------------------------------------------------- -BOOL LLFloaterAnimPreview::validateEaseIn(LLUICtrl* spin, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - if (!previewp->getEnabled()) - return FALSE; - - LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return FALSE; - } - avatarp = gAgentAvatarp; - } - else - { - if (!previewp->mAnimPreview) - { - return FALSE; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); - - if (!motionp->getLoop()) - { - F32 new_ease_in = llclamp((F32)previewp->childGetValue("ease_in_time").asReal(), 0.f, motionp->getDuration() - motionp->getEaseOutDuration()); - previewp->childSetValue("ease_in_time", LLSD(new_ease_in)); - } - - return TRUE; -} - -//----------------------------------------------------------------------------- -// validateEaseOut() -//----------------------------------------------------------------------------- -BOOL LLFloaterAnimPreview::validateEaseOut(LLUICtrl* spin, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - - if (!previewp->getEnabled()) - return FALSE; - - LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return FALSE; - } - avatarp = gAgentAvatarp; - } - else - { - if (!previewp->mAnimPreview) - { - return FALSE; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); - - if (!motionp->getLoop()) - { - F32 new_ease_out = llclamp((F32)previewp->childGetValue("ease_out_time").asReal(), 0.f, motionp->getDuration() - motionp->getEaseInDuration()); - previewp->childSetValue("ease_out_time", LLSD(new_ease_out)); - } - - return TRUE; -} - -//----------------------------------------------------------------------------- -// validateLoopIn() -//----------------------------------------------------------------------------- -BOOL LLFloaterAnimPreview::validateLoopIn(LLUICtrl* ctrl, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - if (!previewp->getEnabled()) - return FALSE; - - F32 loop_in_value = (F32)previewp->childGetValue("loop_in_point").asReal(); - F32 loop_out_value = (F32)previewp->childGetValue("loop_out_point").asReal(); - - if (loop_in_value < 0.f) - { - loop_in_value = 0.f; - } - else if (loop_in_value > 100.f) - { - loop_in_value = 100.f; - } - else if (loop_in_value > loop_out_value) - { - loop_in_value = loop_out_value; - } - - previewp->childSetValue("loop_in_point", LLSD(loop_in_value)); - return TRUE; -} - -//----------------------------------------------------------------------------- -// validateLoopOut() -//----------------------------------------------------------------------------- -BOOL LLFloaterAnimPreview::validateLoopOut(LLUICtrl* spin, void* data) -{ - LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data; - if (!previewp->getEnabled()) - return FALSE; - - F32 loop_out_value = (F32)previewp->childGetValue("loop_out_point").asReal(); - F32 loop_in_value = (F32)previewp->childGetValue("loop_in_point").asReal(); - - if (loop_out_value < 0.f) - { - loop_out_value = 0.f; - } - else if (loop_out_value > 100.f) - { - loop_out_value = 100.f; - } - else if (loop_out_value < loop_in_value) - { - loop_out_value = loop_in_value; - } - - previewp->childSetValue("loop_out_point", LLSD(loop_out_value)); - return TRUE; -} - - -//----------------------------------------------------------------------------- -// refresh() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::refresh() -{ - if (!mAnimPreview) - { - childShow("bad_animation_text"); - mPlayButton->setEnabled(FALSE); - mStopButton->setEnabled(FALSE); - childDisable("ok_btn"); - } - else - { - if (!mInWorld) - { - childHide("bad_animation_text"); - } - mPlayButton->setEnabled(TRUE); - LLVOAvatar* avatarp; - if (mInWorld) - { - avatarp = gAgentAvatarp; - } - else - { - avatarp = mAnimPreview->getDummyAvatar(); - } - if (avatarp->isMotionActive(mMotionID)) - { - mStopButton->setEnabled(TRUE); - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(mMotionID); - if (avatarp->areAnimationsPaused()) - { - - mPlayButton->setImages(std::string("button_anim_play.tga"), - std::string("button_anim_play_selected.tga")); - - } - else - { - if (motionp) - { - F32 fraction_complete = motionp->getLastUpdateTime() / motionp->getDuration(); - childSetValue("playback_slider", fraction_complete); - } - mPlayButton->setImages(std::string("button_anim_pause.tga"), - std::string("button_anim_pause_selected.tga")); - - } - } - else - { - mPauseRequest = avatarp->requestPause(); - mPlayButton->setImages(std::string("button_anim_play.tga"), - std::string("button_anim_play_selected.tga")); - - mStopButton->setEnabled(TRUE); // stop also resets, leave enabled. - } - childEnable("ok_btn"); - if (!mInWorld) - { - mAnimPreview->requestUpdate(); - } - } -} - -//----------------------------------------------------------------------------- -// onBtnOK() -//----------------------------------------------------------------------------- -void LLFloaterAnimPreview::onBtnOK(void* userdata) -{ - LLFloaterAnimPreview* floaterp = (LLFloaterAnimPreview*)userdata; - if (!floaterp->getEnabled()) return; - - if ((!floaterp->mInWorld && floaterp->mAnimPreview) || (floaterp->mInWorld && gAgentAvatarp)) - { - LLKeyframeMotion* motionp; - if (floaterp->mInWorld) - { - motionp = (LLKeyframeMotion*)gAgentAvatarp->findMotion(floaterp->mMotionID); - } - else - { - motionp = (LLKeyframeMotion*)floaterp->mAnimPreview->getDummyAvatar()->findMotion(floaterp->mMotionID); - } - - S32 file_size = motionp->getFileSize(); - U8* buffer = new U8[file_size]; - - LLDataPackerBinaryBuffer dp(buffer, file_size); - if (motionp->serialize(dp)) - { - LLVFile file(gVFS, motionp->getID(), LLAssetType::AT_ANIMATION, LLVFile::APPEND); - - S32 size = dp.getCurrentSize(); - file.setMaxSize(size); - if (file.write((U8*)buffer, size)) - { - std::string name = floaterp->childGetValue("name_form").asString(); - std::string desc = floaterp->childGetValue("description_form").asString(); - LLAssetStorage::LLStoreAssetCallback callback = NULL; - S32 expected_upload_cost = sUploadAmount; - void *userdata = NULL; - - // - if(floaterp->mItem) - { - // Update existing item instead of creating a new one - LLViewerInventoryItem* item = (LLViewerInventoryItem*)floaterp->mItem; - LLSaveInfo* info = new LLSaveInfo(item->getUUID(), LLUUID::null, desc, floaterp->mTransactionID); - gAssetStorage->storeAssetData(floaterp->mTransactionID, LLAssetType::AT_ANIMATION, NULL, info, FALSE); - - // I guess I will do this now because the floater is closing... - LLPointer new_item = new LLViewerInventoryItem(item); - new_item->setDescription(desc); - new_item->setTransactionID(floaterp->mTransactionID); - new_item->setAssetUUID(motionp->getID()); - new_item->updateServer(FALSE); - gInventory.updateItem(new_item); - gInventory.notifyObservers(); - } - else - // - { - upload_new_resource(floaterp->mTransactionID, // tid - LLAssetType::AT_ANIMATION, - name, - desc, - 0, - LLFolderType::FT_NONE, - LLInventoryType::IT_ANIMATION, - LLFloaterPerms::getNextOwnerPerms(), LLFloaterPerms::getGroupPerms(), LLFloaterPerms::getEveryonePerms(), - name, - callback, expected_upload_cost, userdata); - } - } - else - { - llwarns << "Failure writing animation data." << llendl; - LLNotificationsUtil::add("WriteAnimationFail"); - } - } - - delete [] buffer; - // clear out cache for motion data - if (floaterp->mInWorld) - { - gAgentAvatarp->removeMotion(floaterp->mMotionID); - gAgentAvatarp->deactivateAllMotions(); - } - else - { - floaterp->mAnimPreview->getDummyAvatar()->removeMotion(floaterp->mMotionID); - } - LLKeyframeDataCache::removeKeyframeData(floaterp->mMotionID); - } - - floaterp->close(false); -} - -//----------------------------------------------------------------------------- -// LLPreviewAnimation -//----------------------------------------------------------------------------- -LLPreviewAnimation::LLPreviewAnimation(S32 width, S32 height) : LLViewerDynamicTexture(width, height, 3, ORDER_MIDDLE, FALSE) -{ - mNeedsUpdate = TRUE; - mCameraDistance = PREVIEW_CAMERA_DISTANCE; - mCameraYaw = 0.f; - mCameraPitch = 0.f; - mCameraZoom = 1.f; - - mDummyAvatar = (LLVOAvatar*)gObjectList.createObjectViewer(LL_PCODE_LEGACY_AVATAR, gAgent.getRegion()); - mDummyAvatar->createDrawable(&gPipeline); - mDummyAvatar->mIsDummy = TRUE; - mDummyAvatar->mSpecialRenderMode = 1; - mDummyAvatar->setPositionAgent(LLVector3::zero); - mDummyAvatar->slamPosition(); - mDummyAvatar->updateJointLODs(); - mDummyAvatar->updateGeometry(mDummyAvatar->mDrawable); - mDummyAvatar->startMotion(ANIM_AGENT_STAND, BASE_ANIM_TIME_OFFSET); - mDummyAvatar->hideSkirt(); - //gPipeline.markVisible(mDummyAvatar->mDrawable, *LLViewerCamera::getInstance()); - - // stop extraneous animations - mDummyAvatar->stopMotion( ANIM_AGENT_HEAD_ROT, TRUE ); - mDummyAvatar->stopMotion( ANIM_AGENT_EYE, TRUE ); - mDummyAvatar->stopMotion( ANIM_AGENT_BODY_NOISE, TRUE ); - mDummyAvatar->stopMotion( ANIM_AGENT_BREATHE_ROT, TRUE ); -} - -//----------------------------------------------------------------------------- -// LLPreviewAnimation() -//----------------------------------------------------------------------------- -LLPreviewAnimation::~LLPreviewAnimation() -{ - mDummyAvatar->markDead(); -} - -//virtual -S8 LLPreviewAnimation::getType() const -{ - return LLViewerDynamicTexture::LL_PREVIEW_ANIMATION ; -} - -//----------------------------------------------------------------------------- -// update() -//----------------------------------------------------------------------------- -BOOL LLPreviewAnimation::render() -{ - mNeedsUpdate = FALSE; - LLVOAvatar* avatarp = mDummyAvatar; - - gGL.matrixMode(LLRender::MM_PROJECTION); - gGL.pushMatrix(); - gGL.loadIdentity(); - gGL.ortho(0.0f, mFullWidth, 0.0f, mFullHeight, -1.0f, 1.0f); - - gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.pushMatrix(); - gGL.loadIdentity(); - - if (LLGLSLShader::sNoFixedFunction) - { - gUIProgram.bind(); - } - - LLGLSUIDefault def; - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.color4f(0.15f, 0.2f, 0.3f, 1.f); - - gl_rect_2d_simple( mFullWidth, mFullHeight ); - - gGL.matrixMode(LLRender::MM_PROJECTION); - gGL.popMatrix(); - - gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.popMatrix(); - - gGL.flush(); - - LLVector3 target_pos = avatarp->mRoot->getWorldPosition(); - - LLQuaternion camera_rot = LLQuaternion(mCameraPitch, LLVector3::y_axis) * - LLQuaternion(mCameraYaw, LLVector3::z_axis); - - LLQuaternion av_rot = avatarp->mRoot->getWorldRotation() * camera_rot; - LLViewerCamera::getInstance()->setOriginAndLookAt( - target_pos + ((LLVector3(mCameraDistance, 0.f, 0.f) + mCameraOffset) * av_rot), // camera - LLVector3::z_axis, // up - target_pos + (mCameraOffset * av_rot) ); // point of interest - - LLViewerCamera::getInstance()->setView(LLViewerCamera::getInstance()->getDefaultFOV() / mCameraZoom); - LLViewerCamera::getInstance()->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, mFullWidth, mFullHeight, FALSE); - - mCameraRelPos = LLViewerCamera::getInstance()->getOrigin() - avatarp->mHeadp->getWorldPosition(); - - //avatarp->setAnimationData("LookAtPoint", (void *)&mCameraRelPos); - - //SJB: Animation is updated in LLVOAvatar::updateCharacter - - if (avatarp->mDrawable.notNull()) - { - avatarp->updateLOD(); - - LLVertexBuffer::unbind(); - LLGLDepthTest gls_depth(GL_TRUE); - - LLDrawPoolAvatar *avatarPoolp = (LLDrawPoolAvatar *)avatarp->mDrawable->getFace(0)->getPool(); - avatarp->dirtyMesh(); - avatarPoolp->renderAvatars(avatarp); // renders only one avatar - } - - gGL.color4f(1,1,1,1); - return TRUE; -} - -//----------------------------------------------------------------------------- -// requestUpdate() -//----------------------------------------------------------------------------- -void LLPreviewAnimation::requestUpdate() -{ - mNeedsUpdate = TRUE; -} - -//----------------------------------------------------------------------------- -// rotate() -//----------------------------------------------------------------------------- -void LLPreviewAnimation::rotate(F32 yaw_radians, F32 pitch_radians) -{ - mCameraYaw = mCameraYaw + yaw_radians; - - mCameraPitch = llclamp(mCameraPitch + pitch_radians, F_PI_BY_TWO * -0.8f, F_PI_BY_TWO * 0.8f); -} - -//----------------------------------------------------------------------------- -// zoom() -//----------------------------------------------------------------------------- -void LLPreviewAnimation::zoom(F32 zoom_delta) -{ - setZoom(mCameraZoom + zoom_delta); -} - -//----------------------------------------------------------------------------- -// setZoom() -//----------------------------------------------------------------------------- -void LLPreviewAnimation::setZoom(F32 zoom_amt) -{ - mCameraZoom = llclamp(zoom_amt, MIN_CAMERA_ZOOM, MAX_CAMERA_ZOOM); -} - -//----------------------------------------------------------------------------- -// pan() -//----------------------------------------------------------------------------- -void LLPreviewAnimation::pan(F32 right, F32 up) -{ - mCameraOffset.mV[VY] = llclamp(mCameraOffset.mV[VY] + right * mCameraDistance / mCameraZoom, -1.f, 1.f); - mCameraOffset.mV[VZ] = llclamp(mCameraOffset.mV[VZ] + up * mCameraDistance / mCameraZoom, -1.f, 1.f); -} - - - diff --git a/indra/newview/llfloateranimpreview.h b/indra/newview/llfloateranimpreview.h index 6390fe161..7603286e7 100644 --- a/indra/newview/llfloateranimpreview.h +++ b/indra/newview/llfloateranimpreview.h @@ -2,9 +2,7 @@ * @file llfloateranimpreview.h * @brief LLFloaterAnimPreview class definition * - * $LicenseInfo:firstyear=2004&license=viewergpl$ - * - * Copyright (c) 2004-2009, Linden Research, Inc. + * Copyright (c) 2012, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab @@ -34,112 +32,11 @@ #define LL_LLFLOATERANIMPREVIEW_H #include "llfloaternamedesc.h" -#include "lldynamictexture.h" -#include "llcharacter.h" -#include "llquaternion.h" -class LLVOAvatar; -class LLViewerJointMesh; - -class LLPreviewAnimation : public LLViewerDynamicTexture -{ -protected: - virtual ~LLPreviewAnimation(); - -public: - LLPreviewAnimation(S32 width, S32 height); - - /*virtual*/ S8 getType() const ; - - BOOL render(); - void requestUpdate(); - void rotate(F32 yaw_radians, F32 pitch_radians); - void zoom(F32 zoom_delta); - void setZoom(F32 zoom_amt); - void pan(F32 right, F32 up); - virtual BOOL needsUpdate() { return mNeedsUpdate; } - - LLVOAvatar* getDummyAvatar() { return mDummyAvatar; } - -protected: - BOOL mNeedsUpdate; - F32 mCameraDistance; - F32 mCameraYaw; - F32 mCameraPitch; - F32 mCameraZoom; - LLVector3 mCameraOffset; - LLVector3 mCameraRelPos; - LLPointer mDummyAvatar; -}; - -class LLFloaterAnimPreview : public LLFloaterNameDesc -{ -public: - // - LLFloaterAnimPreview(const std::string& filename, void* item = NULL); - // - virtual ~LLFloaterAnimPreview(); - - BOOL postBuild(); - - 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); - void onMouseCaptureLost(); - - void refresh(); - - static void onBtnPlay(void*); - static void onBtnStop(void*); - static void setUploadAmount(S32 amount) { sUploadAmount = amount; } - static void onSliderMove(LLUICtrl*, void*); - static void onCommitBaseAnim(LLUICtrl*, void*); - static void onCommitLoop(LLUICtrl*, void*); - static void onCommitLoopIn(LLUICtrl*, void*); - static void onCommitLoopOut(LLUICtrl*, void*); - static BOOL validateLoopIn(LLUICtrl*, void*); - static BOOL validateLoopOut(LLUICtrl*, void*); - static void onCommitName(LLUICtrl*, void*); - static void onCommitHandPose(LLUICtrl*, void*); - static void onCommitEmote(LLUICtrl*, void*); - static void onCommitPriority(LLUICtrl*, void*); - static void onCommitEaseIn(LLUICtrl*, void*); - static void onCommitEaseOut(LLUICtrl*, void*); - static BOOL validateEaseIn(LLUICtrl*, void*); - static BOOL validateEaseOut(LLUICtrl*, void*); - static void onBtnOK(void*); - static void onSaveComplete(const LLUUID& asset_uuid, - LLAssetType::EType type, - void* user_data, - S32 status, LLExtStat ext_status); -private: - void setAnimCallbacks() ; - -protected: - void draw(); - void resetMotion(); - - LLPointer< LLPreviewAnimation> mAnimPreview; - S32 mLastMouseX; - S32 mLastMouseY; - LLButton* mPlayButton; - LLButton* mStopButton; - LLRect mPreviewRect; - LLRectf mPreviewImageRect; - LLAssetID mMotionID; - LLTransactionID mTransactionID; - BOOL mEnabled; - BOOL mInWorld; - LLAnimPauseRequest mPauseRequest; - - std::map mIDList; - - static S32 sUploadAmount; - - // - void* mItem; - // +class LLFloaterAnimPreview : public LLFloaterNameDesc { + public: + LLFloaterAnimPreview(LLSD const& filename); + virtual BOOL postBuild(void); }; #endif // LL_LLFLOATERANIMPREVIEW_H diff --git a/indra/newview/llfloaterbvhpreview.cpp b/indra/newview/llfloaterbvhpreview.cpp new file mode 100644 index 000000000..ced2258fc --- /dev/null +++ b/indra/newview/llfloaterbvhpreview.cpp @@ -0,0 +1,1630 @@ +/** + * @file llfloaterbvhpreview.cpp + * @brief LLFloaterBvhPreview class implementation + * + * $LicenseInfo:firstyear=2004&license=viewergpl$ + * + * Copyright (c) 2004-2009, 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://secondlifegrid.net/programs/open_source/licensing/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://secondlifegrid.net/programs/open_source/licensing/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. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloaterbvhpreview.h" + +#include "llbvhloader.h" +#include "lldatapacker.h" +#include "lldir.h" +#include "llnotificationsutil.h" +#include "llvfile.h" +#include "llapr.h" +#include "llstring.h" + +#include "llagent.h" +#include "llanimationstates.h" +#include "llbbox.h" +#include "llbutton.h" +#include "llcheckboxctrl.h" +#include "llcombobox.h" +#include "lldrawable.h" +#include "lldrawpoolavatar.h" +#include "llrender.h" +#include "llface.h" +#include "llfocusmgr.h" +#include "llkeyframemotion.h" +#include "lllineeditor.h" +#include "llfloaterperms.h" +#include "llsliderctrl.h" +#include "llspinctrl.h" +#include "lltextbox.h" +#include "lltoolmgr.h" +#include "llui.h" +#include "llviewercamera.h" +#include "llviewerobjectlist.h" +#include "llviewerwindow.h" +#include "llviewermenufile.h" // upload_new_resource() +#include "llvoavatarself.h" +#include "pipeline.h" +#include "lluictrlfactory.h" +#include "llviewercontrol.h" + +#include "hippogridmanager.h" + +// +#include "llinventorymodel.h" // gInventoryModel +// + +S32 LLFloaterBvhPreview::sUploadAmount = 10; + +const S32 PREVIEW_BORDER_WIDTH = 2; +const S32 PREVIEW_RESIZE_HANDLE_SIZE = S32(RESIZE_HANDLE_WIDTH * OO_SQRT2) + PREVIEW_BORDER_WIDTH; +const S32 PREVIEW_HPAD = PREVIEW_RESIZE_HANDLE_SIZE; +const S32 PREF_BUTTON_HEIGHT = 16; +const S32 PREVIEW_TEXTURE_HEIGHT = 300; + +const F32 PREVIEW_CAMERA_DISTANCE = 4.f; + +const F32 MIN_CAMERA_ZOOM = 0.5f; +const F32 MAX_CAMERA_ZOOM = 10.f; + +const F32 BASE_ANIM_TIME_OFFSET = 5.f; + +// +struct LLSaveInfo +{ + LLSaveInfo(const LLUUID& item_id, const LLUUID& object_id, const std::string& desc, + const LLTransactionID tid) + : mItemUUID(item_id), mObjectUUID(object_id), mDesc(desc), mTransactionID(tid) + { + } + + LLUUID mItemUUID; + LLUUID mObjectUUID; + std::string mDesc; + LLTransactionID mTransactionID; +}; +// + +std::string STATUS[] = +{ + "E_ST_OK", + "E_ST_EOF", + "E_ST_NO_CONSTRAINT", + "E_ST_NO_FILE", + "E_ST_NO_HIER", + "E_ST_NO_JOINT", + "E_ST_NO_NAME", + "E_ST_NO_OFFSET", + "E_ST_NO_CHANNELS", + "E_ST_NO_ROTATION", + "E_ST_NO_AXIS", + "E_ST_NO_MOTION", + "E_ST_NO_FRAMES", + "E_ST_NO_FRAME_TIME", + "E_ST_NO_POS", + "E_ST_NO_ROT", + "E_ST_NO_XLT_FILE", + "E_ST_NO_XLT_HEADER", + "E_ST_NO_XLT_NAME", + "E_ST_NO_XLT_IGNORE", + "E_ST_NO_XLT_RELATIVE", + "E_ST_NO_XLT_OUTNAME", + "E_ST_NO_XLT_MATRIX", + "E_ST_NO_XLT_MERGECHILD", + "E_ST_NO_XLT_MERGEPARENT", + "E_ST_NO_XLT_PRIORITY", + "E_ST_NO_XLT_LOOP", + "E_ST_NO_XLT_EASEIN", + "E_ST_NO_XLT_EASEOUT", + "E_ST_NO_XLT_HAND", + "E_ST_NO_XLT_EMOTE", +"E_ST_BAD_ROOT" +}; + + +//----------------------------------------------------------------------------- +// LLFloaterBvhPreview() +//----------------------------------------------------------------------------- +LLFloaterBvhPreview::LLFloaterBvhPreview(const std::string& filename, void* item) : + LLFloaterNameDesc(filename) +{ + // + mItem = item; + // + + mLastMouseX = 0; + mLastMouseY = 0; + + mIDList["Standing"] = ANIM_AGENT_STAND; + mIDList["Walking"] = ANIM_AGENT_FEMALE_WALK; + mIDList["Sitting"] = ANIM_AGENT_SIT_FEMALE; + mIDList["Flying"] = ANIM_AGENT_HOVER; + + mIDList["[None]"] = LLUUID::null; + mIDList["Aaaaah"] = ANIM_AGENT_EXPRESS_OPEN_MOUTH; + mIDList["Afraid"] = ANIM_AGENT_EXPRESS_AFRAID; + mIDList["Angry"] = ANIM_AGENT_EXPRESS_ANGER; + mIDList["Big Smile"] = ANIM_AGENT_EXPRESS_TOOTHSMILE; + mIDList["Bored"] = ANIM_AGENT_EXPRESS_BORED; + mIDList["Cry"] = ANIM_AGENT_EXPRESS_CRY; + mIDList["Disdain"] = ANIM_AGENT_EXPRESS_DISDAIN; + mIDList["Embarrassed"] = ANIM_AGENT_EXPRESS_EMBARRASSED; + mIDList["Frown"] = ANIM_AGENT_EXPRESS_FROWN; + mIDList["Kiss"] = ANIM_AGENT_EXPRESS_KISS; + mIDList["Laugh"] = ANIM_AGENT_EXPRESS_LAUGH; + mIDList["Plllppt"] = ANIM_AGENT_EXPRESS_TONGUE_OUT; + mIDList["Repulsed"] = ANIM_AGENT_EXPRESS_REPULSED; + mIDList["Sad"] = ANIM_AGENT_EXPRESS_SAD; + mIDList["Shrug"] = ANIM_AGENT_EXPRESS_SHRUG; + mIDList["Smile"] = ANIM_AGENT_EXPRESS_SMILE; + mIDList["Surprise"] = ANIM_AGENT_EXPRESS_SURPRISE; + mIDList["Wink"] = ANIM_AGENT_EXPRESS_WINK; + mIDList["Worry"] = ANIM_AGENT_EXPRESS_WORRY; +} + +//----------------------------------------------------------------------------- +// setAnimCallbacks() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::setAnimCallbacks() +{ + childSetCommitCallback("playback_slider", onSliderMove, this); + + childSetCommitCallback("preview_base_anim", onCommitBaseAnim, this); + childSetValue("preview_base_anim", "Standing"); + + childSetCommitCallback("priority", onCommitPriority, this); + childSetCommitCallback("loop_check", onCommitLoop, this); + childSetCommitCallback("loop_in_point", onCommitLoopIn, this); + childSetValidate("loop_in_point", validateLoopIn); + childSetCommitCallback("loop_out_point", onCommitLoopOut, this); + childSetValidate("loop_out_point", validateLoopOut); + + childSetCommitCallback("hand_pose_combo", onCommitHandPose, this); + + childSetCommitCallback("emote_combo", onCommitEmote, this); + childSetValue("emote_combo", "[None]"); + + childSetCommitCallback("ease_in_time", onCommitEaseIn, this); + childSetValidate("ease_in_time", validateEaseIn); + childSetCommitCallback("ease_out_time", onCommitEaseOut, this); + childSetValidate("ease_out_time", validateEaseOut); +} + +//----------------------------------------------------------------------------- +// postBuild() +//----------------------------------------------------------------------------- +BOOL LLFloaterBvhPreview::postBuild() +{ + LLRect r; + LLKeyframeMotion* motionp = NULL; + LLBVHLoader* loaderp = NULL; + + if (!LLFloaterNameDesc::postBuild()) + { + return FALSE; + } + + mInWorld = gSavedSettings.getBOOL("PreviewAnimInWorld"); + + childSetCommitCallback("name_form", onCommitName, this); + + if (gSavedSettings.getBOOL("AscentPowerfulWizard")) + { + childSetMaxValue("priority", 7); + } + + childSetLabelArg("ok_btn", "[UPLOADFEE]", gHippoGridManager->getConnectedGrid()->getUploadFee()); + childSetAction("ok_btn", onBtnOK, this); + setDefaultBtn(); + + if (mInWorld) + { + r = getRect(); + translate(0, 230); + reshape(r.getWidth(), r.getHeight() - 230); + childSetValue("bad_animation_text", getString("in_world")); + childShow("bad_animation_text"); + } + else + { + childHide("bad_animation_text"); + } + + mPreviewRect.set(PREVIEW_HPAD, + PREVIEW_TEXTURE_HEIGHT, + getRect().getWidth() - PREVIEW_HPAD, + PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD); + mPreviewImageRect.set(0.f, 1.f, 1.f, 0.f); + + S32 y = mPreviewRect.mTop + BTN_HEIGHT; + S32 btn_left = PREVIEW_HPAD; + + r.set( btn_left, y, btn_left + 32, y - BTN_HEIGHT ); + mPlayButton = getChild( "play_btn"); + if (!mPlayButton) + { + mPlayButton = new LLButton(std::string("play_btn"), LLRect(0,0,0,0)); + } + mPlayButton->setClickedCallback(boost::bind(&LLFloaterBvhPreview::onBtnPlay,this)); + + mPlayButton->setImages(std::string("button_anim_play.tga"), + std::string("button_anim_play_selected.tga")); + + mPlayButton->setImageDisabled(NULL); + mPlayButton->setImageDisabledSelected(NULL); + + mPlayButton->setScaleImage(TRUE); + + mStopButton = getChild( "stop_btn"); + if (!mStopButton) + { + mStopButton = new LLButton(std::string("stop_btn"), LLRect(0,0,0,0)); + } + mStopButton->setClickedCallback(boost::bind(&LLFloaterBvhPreview::onBtnStop, this)); + + mStopButton->setImages(std::string("button_anim_stop.tga"), + std::string("button_anim_stop_selected.tga")); + + mStopButton->setImageDisabled(NULL); + mStopButton->setImageDisabledSelected(NULL); + + mStopButton->setScaleImage(TRUE); + + r.set(r.mRight + PREVIEW_HPAD, y, getRect().getWidth() - PREVIEW_HPAD, y - BTN_HEIGHT); + //childSetCommitCallback("playback_slider", onSliderMove, this); + + //childSetCommitCallback("preview_base_anim", onCommitBaseAnim, this); + //childSetValue("preview_base_anim", "Standing"); + + //childSetCommitCallback("priority", onCommitPriority, this); + //childSetCommitCallback("loop_check", onCommitLoop, this); + //childSetCommitCallback("loop_in_point", onCommitLoopIn, this); + //childSetValidate("loop_in_point", validateLoopIn); + //childSetCommitCallback("loop_out_point", onCommitLoopOut, this); + //childSetValidate("loop_out_point", validateLoopOut); + + //childSetCommitCallback("hand_pose_combo", onCommitHandPose, this); + + //childSetCommitCallback("emote_combo", onCommitEmote, this); + //childSetValue("emote_combo", "[None]"); + + //childSetCommitCallback("ease_in_time", onCommitEaseIn, this); + //childSetValidate("ease_in_time", validateEaseIn); + //childSetCommitCallback("ease_out_time", onCommitEaseOut, this); + //childSetValidate("ease_out_time", validateEaseOut); + + // moved declaration from below + BOOL success = false; + // + + std::string exten = gDirUtilp->getExtension(mFilename); + if (exten == "bvh") + { + // loading a bvh file + + // now load bvh file + S32 file_size; + + LLAPRFile infile(mFilenameAndPath, LL_APR_RB, &file_size); + + if (!infile.getFileHandle()) + { + llwarns << "Can't open BVH file:" << mFilename << llendl; + } + else + { + char* file_buffer; + + file_buffer = new char[file_size + 1]; + + if (file_size == infile.read(file_buffer, file_size)) + { + file_buffer[file_size] = '\0'; + llinfos << "Loading BVH file " << mFilename << llendl; + ELoadStatus load_status = E_ST_OK; + S32 line_number = 0; + loaderp = new LLBVHLoader(file_buffer, load_status, line_number); + std::string status = getString(STATUS[load_status]); + + if(load_status == E_ST_NO_XLT_FILE) + { + llwarns << "NOTE: No translation table found." << llendl; + } + else + { + llwarns << "ERROR: [line: " << line_number << "] " << status << llendl; + } + } + + infile.close() ; + delete[] file_buffer; + + // moved everything bvh from below + if(loaderp && loaderp->isInitialized() && loaderp->getDuration() <= MAX_ANIM_DURATION) + { + mTransactionID.generate(); + mMotionID = mTransactionID.makeAssetID(gAgent.getSecureSessionID()); + + mAnimPreview = new LLPreviewAnimation(256, 256); + + // motion will be returned, but it will be in a load-pending state, as this is a new motion + // this motion will not request an asset transfer until next update, so we have a chance to + // load the keyframe data locally + if (mInWorld) + { + motionp = (LLKeyframeMotion*)gAgentAvatarp->createMotion(mMotionID); + } + else + { + motionp = (LLKeyframeMotion*)mAnimPreview->getDummyAvatar()->createMotion(mMotionID); + } + + // create data buffer for keyframe initialization + S32 buffer_size = loaderp->getOutputSize(); + U8* buffer = new U8[buffer_size]; + + LLDataPackerBinaryBuffer dp(buffer, buffer_size); + + // pass animation data through memory buffer + loaderp->serialize(dp); + dp.reset(); + success = motionp && motionp->deserialize(dp); + } + else + { + success = false; + if ( loaderp ) + { + if (loaderp->getDuration() > MAX_ANIM_DURATION) + { + LLUIString out_str = getString("anim_too_long"); + out_str.setArg("[LENGTH]", llformat("%.1f", loaderp->getDuration())); + out_str.setArg("[MAX_LENGTH]", llformat("%.1f", MAX_ANIM_DURATION)); + getChild("bad_animation_text")->setValue(out_str.getString()); + } + else + { + LLUIString out_str = getString("failed_file_read"); + out_str.setArg("[STATUS]", getString(STATUS[loaderp->getStatus()])); + getChild("bad_animation_text")->setValue(out_str.getString()); + } + } + + //setEnabled(FALSE); + mMotionID.setNull(); + mAnimPreview = NULL; + } + // + } + } + // + else if(exten == "animatn") + { + S32 file_size; + LLAPRFile raw_animatn(mFilenameAndPath, LL_APR_RB, &file_size); + + if (!raw_animatn.getFileHandle()) + { + llwarns << "Can't open animatn file:" << mFilename << llendl; + } + else + { + char* file_buffer; + + file_buffer = new char[file_size + 1]; + + if (file_size == raw_animatn.read(file_buffer, file_size)) + { + file_buffer[file_size] = '\0'; + llinfos << "Loading animatn file " << mFilename << llendl; + mTransactionID.generate(); + mMotionID = mTransactionID.makeAssetID(gAgent.getSecureSessionID()); + mAnimPreview = new LLPreviewAnimation(256, 256); + motionp = (LLKeyframeMotion*)mAnimPreview->getDummyAvatar()->createMotion(mMotionID); + LLDataPackerBinaryBuffer dp((U8*)file_buffer, file_size); + dp.reset(); + success = motionp && motionp->deserialize(dp); + } + + raw_animatn.close(); + delete[] file_buffer; + } + } + // + + if (success) + { + setAnimCallbacks() ; + + if (!mInWorld) + { + const LLBBoxLocal &pelvis_bbox = motionp->getPelvisBBox(); + + LLVector3 temp = pelvis_bbox.getCenter(); + // only consider XY? + //temp.mV[VZ] = 0.f; + F32 pelvis_offset = temp.magVec(); + + temp = pelvis_bbox.getExtent(); + //temp.mV[VZ] = 0.f; + F32 pelvis_max_displacement = pelvis_offset + (temp.magVec() * 0.5f) + 1.f; + + F32 camera_zoom = LLViewerCamera::getInstance()->getDefaultFOV() / (2.f * atan(pelvis_max_displacement / PREVIEW_CAMERA_DISTANCE)); + + mAnimPreview->setZoom(camera_zoom); + } + + motionp->setName(childGetValue("name_form").asString()); + if (!mInWorld) + { + mAnimPreview->getDummyAvatar()->startMotion(mMotionID); + } + childSetMinValue("playback_slider", 0.0); + childSetMaxValue("playback_slider", 1.0); + + childSetValue("loop_check", LLSD(motionp->getLoop())); + childSetValue("loop_in_point", LLSD(motionp->getLoopIn() / motionp->getDuration() * 100.f)); + childSetValue("loop_out_point", LLSD(motionp->getLoopOut() / motionp->getDuration() * 100.f)); + childSetValue("priority", LLSD((F32)motionp->getPriority())); + childSetValue("hand_pose_combo", LLHandMotion::getHandPoseName(motionp->getHandPose())); + childSetValue("ease_in_time", LLSD(motionp->getEaseInDuration())); + childSetValue("ease_out_time", LLSD(motionp->getEaseOutDuration())); + setEnabled(TRUE); + std::string seconds_string; + seconds_string = llformat(" - %.2f seconds", motionp->getDuration()); + + setTitle(mFilename + std::string(seconds_string)); + } + else + { + mAnimPreview = NULL; + mMotionID.setNull(); + childSetValue("bad_animation_text", getString("failed_to_initialize")); + } + + + refresh(); + + delete loaderp; + + return TRUE; +} + +//----------------------------------------------------------------------------- +// LLFloaterBvhPreview() +//----------------------------------------------------------------------------- +LLFloaterBvhPreview::~LLFloaterBvhPreview() +{ + if (mInWorld) + { + LLVOAvatar* avatarp = gAgentAvatarp; + if (avatarp) + { + if (mMotionID.notNull()) + { + avatarp->stopMotion(mMotionID, TRUE); + avatarp->removeMotion(mMotionID); + } + avatarp->deactivateAllMotions(); + avatarp->startMotion(ANIM_AGENT_HEAD_ROT); + avatarp->startMotion(ANIM_AGENT_EYE); + avatarp->startMotion(ANIM_AGENT_BODY_NOISE); + avatarp->startMotion(ANIM_AGENT_BREATHE_ROT); + avatarp->startMotion(ANIM_AGENT_HAND_MOTION); + avatarp->startMotion(ANIM_AGENT_PELVIS_FIX); + avatarp->startMotion(ANIM_AGENT_STAND, BASE_ANIM_TIME_OFFSET); + } + } + mAnimPreview = NULL; + + setEnabled(FALSE); +} + +//----------------------------------------------------------------------------- +// draw() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::draw() +{ + LLFloater::draw(); + LLRect r = getRect(); + + refresh(); + + if (mMotionID.notNull() && mAnimPreview && !mInWorld) + { + gGL.color3f(1.f, 1.f, 1.f); + + gGL.getTexUnit(0)->bind(mAnimPreview); + + gGL.begin( LLRender::QUADS ); + { + gGL.texCoord2f(0.f, 1.f); + gGL.vertex2i(PREVIEW_HPAD, PREVIEW_TEXTURE_HEIGHT); + gGL.texCoord2f(0.f, 0.f); + gGL.vertex2i(PREVIEW_HPAD, PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD); + gGL.texCoord2f(1.f, 0.f); + gGL.vertex2i(r.getWidth() - PREVIEW_HPAD, PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD); + gGL.texCoord2f(1.f, 1.f); + gGL.vertex2i(r.getWidth() - PREVIEW_HPAD, PREVIEW_TEXTURE_HEIGHT); + } + gGL.end(); + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + LLVOAvatar* avatarp = mAnimPreview->getDummyAvatar(); + if (!avatarp->areAnimationsPaused()) + { + mAnimPreview->requestUpdate(); + } + } +} + +//----------------------------------------------------------------------------- +// resetMotion() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::resetMotion() +{ + LLVOAvatar* avatarp; + if (mInWorld) + { + avatarp = gAgentAvatarp; + } + else + { + avatarp = mAnimPreview->getDummyAvatar(); + } + if (!avatarp) + { + return; + } + + BOOL paused = avatarp->areAnimationsPaused(); + + // *TODO: Fix awful casting hack + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(mMotionID); + + // Set emotion + std::string emote = childGetValue("emote_combo").asString(); + motionp->setEmote(mIDList[emote]); + + LLUUID base_id = mIDList[childGetValue("preview_base_anim").asString()]; + avatarp->deactivateAllMotions(); + avatarp->startMotion(base_id, BASE_ANIM_TIME_OFFSET); + avatarp->startMotion(mMotionID, 0.0f); + childSetValue("playback_slider", 0.0f); + + // Set pose + std::string handpose = childGetValue("hand_pose_combo").asString(); + avatarp->startMotion( ANIM_AGENT_HAND_MOTION, 0.0f ); + motionp->setHandPose(LLHandMotion::getHandPose(handpose)); + + if (paused) + { + mPauseRequest = avatarp->requestPause(); + } + else + { + mPauseRequest = NULL; + } +} + +//----------------------------------------------------------------------------- +// handleMouseDown() +//----------------------------------------------------------------------------- +BOOL LLFloaterBvhPreview::handleMouseDown(S32 x, S32 y, MASK mask) +{ + if (!mInWorld && mPreviewRect.pointInRect(x, y)) + { + bringToFront( x, y ); + gFocusMgr.setMouseCapture(this); + gViewerWindow->hideCursor(); + mLastMouseX = x; + mLastMouseY = y; + return TRUE; + } + + return LLFloater::handleMouseDown(x, y, mask); +} + +//----------------------------------------------------------------------------- +// handleMouseUp() +//----------------------------------------------------------------------------- +BOOL LLFloaterBvhPreview::handleMouseUp(S32 x, S32 y, MASK mask) +{ + if (!mInWorld) + { + gFocusMgr.setMouseCapture(FALSE); + gViewerWindow->showCursor(); + } + return LLFloater::handleMouseUp(x, y, mask); +} + +//----------------------------------------------------------------------------- +// handleHover() +//----------------------------------------------------------------------------- +BOOL LLFloaterBvhPreview::handleHover(S32 x, S32 y, MASK mask) +{ + if (mInWorld) + { + return TRUE; + } + + MASK local_mask = mask & ~MASK_ALT; + + if (mAnimPreview && hasMouseCapture()) + { + if (local_mask == MASK_PAN) + { + // pan here + mAnimPreview->pan((F32)(x - mLastMouseX) * -0.005f, (F32)(y - mLastMouseY) * -0.005f); + } + else if (local_mask == MASK_ORBIT) + { + F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f; + F32 pitch_radians = (F32)(y - mLastMouseY) * 0.02f; + + mAnimPreview->rotate(yaw_radians, pitch_radians); + } + else + { + F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f; + F32 zoom_amt = (F32)(y - mLastMouseY) * 0.02f; + + mAnimPreview->rotate(yaw_radians, 0.f); + mAnimPreview->zoom(zoom_amt); + } + + mAnimPreview->requestUpdate(); + + LLUI::setMousePositionLocal(this, mLastMouseX, mLastMouseY); + } + + if (!mPreviewRect.pointInRect(x, y) || !mAnimPreview) + { + return LLFloater::handleHover(x, y, mask); + } + else if (local_mask == MASK_ORBIT) + { + gViewerWindow->setCursor(UI_CURSOR_TOOLCAMERA); + } + else if (local_mask == MASK_PAN) + { + gViewerWindow->setCursor(UI_CURSOR_TOOLPAN); + } + else + { + gViewerWindow->setCursor(UI_CURSOR_TOOLZOOMIN); + } + + return TRUE; +} + +//----------------------------------------------------------------------------- +// handleScrollWheel() +//----------------------------------------------------------------------------- +BOOL LLFloaterBvhPreview::handleScrollWheel(S32 x, S32 y, S32 clicks) +{ + if (!mInWorld) + { + mAnimPreview->zoom((F32)clicks * -0.2f); + mAnimPreview->requestUpdate(); + } + return TRUE; +} + +//----------------------------------------------------------------------------- +// onMouseCaptureLost() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onMouseCaptureLost() +{ + if (!mInWorld) + { + gViewerWindow->showCursor(); + } +} + +//----------------------------------------------------------------------------- +// onBtnPlay() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onBtnPlay(void* user_data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)user_data; + if (!previewp->getEnabled()) + return; + + if (previewp->mMotionID.notNull()) + { + LLVOAvatar* avatarp; + if (previewp->mInWorld) + { + if (!gAgentAvatarp) + { + return; + } + avatarp = gAgentAvatarp; + } + else + { + if (!previewp->mAnimPreview) + { + return; + } + avatarp = previewp->mAnimPreview->getDummyAvatar(); + } + + if(!avatarp->isMotionActive(previewp->mMotionID)) + { + previewp->resetMotion(); + previewp->mPauseRequest = NULL; + } + else + { + if (avatarp->areAnimationsPaused()) + { + previewp->mPauseRequest = NULL; + } + else + { + previewp->mPauseRequest = avatarp->requestPause(); + } + } + } +} + +//----------------------------------------------------------------------------- +// onBtnStop() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onBtnStop(void* user_data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)user_data; + if (!previewp->getEnabled()) + return; + + if (previewp->mMotionID.notNull()) + { + LLVOAvatar* avatarp; + if (previewp->mInWorld) + { + if (!gAgentAvatarp) + { + return; + } + avatarp = gAgentAvatarp; + } + else + { + if (!previewp->mAnimPreview) + { + return; + } + avatarp = previewp->mAnimPreview->getDummyAvatar(); + } + previewp->resetMotion(); + previewp->mPauseRequest = avatarp->requestPause(); + } +} + +//----------------------------------------------------------------------------- +// onSliderMove() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onSliderMove(LLUICtrl* ctrl, void*user_data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)user_data; + if (!previewp->getEnabled()) + return; + + LLVOAvatar* avatarp; + if (previewp->mInWorld) + { + if (!gAgentAvatarp) + { + return; + } + avatarp = gAgentAvatarp; + } + else + { + if (!previewp->mAnimPreview) + { + return; + } + avatarp = previewp->mAnimPreview->getDummyAvatar(); + } + F32 slider_value = (F32)previewp->childGetValue("playback_slider").asReal(); + LLUUID base_id = previewp->mIDList[previewp->childGetValue("preview_base_anim").asString()]; + LLMotion* motionp = avatarp->findMotion(previewp->mMotionID); + F32 duration = motionp->getDuration();// + motionp->getEaseOutDuration(); + F32 delta_time = duration * slider_value; + avatarp->deactivateAllMotions(); + avatarp->startMotion(base_id, delta_time + BASE_ANIM_TIME_OFFSET); + avatarp->startMotion(previewp->mMotionID, delta_time); + previewp->mPauseRequest = avatarp->requestPause(); + previewp->refresh(); +} + +//----------------------------------------------------------------------------- +// onCommitBaseAnim() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onCommitBaseAnim(LLUICtrl* ctrl, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + if (!previewp->getEnabled()) + return; + + LLVOAvatar* avatarp; + if (previewp->mInWorld) + { + if (!gAgentAvatarp) + { + return; + } + avatarp = gAgentAvatarp; + } + else + { + if (!previewp->mAnimPreview) + { + return; + } + avatarp = previewp->mAnimPreview->getDummyAvatar(); + } + + BOOL paused = avatarp->areAnimationsPaused(); + + // stop all other possible base motions + avatarp->stopMotion(ANIM_AGENT_STAND, TRUE); + avatarp->stopMotion(ANIM_AGENT_WALK, TRUE); + avatarp->stopMotion(ANIM_AGENT_SIT, TRUE); + avatarp->stopMotion(ANIM_AGENT_HOVER, TRUE); + + previewp->resetMotion(); + + if (!paused) + { + previewp->mPauseRequest = NULL; + } +} + +//----------------------------------------------------------------------------- +// onCommitLoop() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onCommitLoop(LLUICtrl* ctrl, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + if (!previewp->getEnabled()) + return; + + LLVOAvatar* avatarp; + if (previewp->mInWorld) + { + if (!gAgentAvatarp) + { + return; + } + avatarp = gAgentAvatarp; + } + else + { + if (!previewp->mAnimPreview) + { + return; + } + avatarp = previewp->mAnimPreview->getDummyAvatar(); + } + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + + if (motionp) + { + motionp->setLoop(previewp->childGetValue("loop_check").asBoolean()); + motionp->setLoopIn((F32)previewp->childGetValue("loop_in_point").asReal() * 0.01f * motionp->getDuration()); + motionp->setLoopOut((F32)previewp->childGetValue("loop_out_point").asReal() * 0.01f * motionp->getDuration()); + } +} + +//----------------------------------------------------------------------------- +// onCommitLoopIn() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onCommitLoopIn(LLUICtrl* ctrl, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + if (!previewp->getEnabled()) + return; + + LLVOAvatar* avatarp; + if (previewp->mInWorld) + { + if (!gAgentAvatarp) + { + return; + } + avatarp = gAgentAvatarp; + } + else + { + if (!previewp->mAnimPreview) + { + return; + } + avatarp = previewp->mAnimPreview->getDummyAvatar(); + } + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + + if (motionp) + { + motionp->setLoopIn((F32)previewp->childGetValue("loop_in_point").asReal() / 100.f); + previewp->resetMotion(); + previewp->childSetValue("loop_check", LLSD(TRUE)); + onCommitLoop(ctrl, data); + } +} + +//----------------------------------------------------------------------------- +// onCommitLoopOut() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onCommitLoopOut(LLUICtrl* ctrl, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + if (!previewp->getEnabled()) + return; + + LLVOAvatar* avatarp; + if (previewp->mInWorld) + { + if (!gAgentAvatarp) + { + return; + } + avatarp = gAgentAvatarp; + } + else + { + if (!previewp->mAnimPreview) + { + return; + } + avatarp = previewp->mAnimPreview->getDummyAvatar(); + } + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + + if (motionp) + { + motionp->setLoopOut((F32)previewp->childGetValue("loop_out_point").asReal() * 0.01f * motionp->getDuration()); + previewp->resetMotion(); + previewp->childSetValue("loop_check", LLSD(TRUE)); + onCommitLoop(ctrl, data); + } +} + +//----------------------------------------------------------------------------- +// onCommitName() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onCommitName(LLUICtrl* ctrl, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + if (!previewp->getEnabled()) + return; + + LLVOAvatar* avatarp; + if (previewp->mInWorld) + { + if (!gAgentAvatarp) + { + return; + } + avatarp = gAgentAvatarp; + } + else + { + if (!previewp->mAnimPreview) + { + return; + } + avatarp = previewp->mAnimPreview->getDummyAvatar(); + } + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + + if (motionp) + { + motionp->setName(previewp->childGetValue("name_form").asString()); + } + + LLFloaterNameDesc::doCommit(ctrl, data); +} + +//----------------------------------------------------------------------------- +// onCommitHandPose() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onCommitHandPose(LLUICtrl* ctrl, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + if (!previewp->getEnabled()) + return; + + previewp->resetMotion(); // sets hand pose +} + +//----------------------------------------------------------------------------- +// onCommitEmote() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onCommitEmote(LLUICtrl* ctrl, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + if (!previewp->getEnabled()) + return; + + previewp->resetMotion(); // ssts emote +} + +//----------------------------------------------------------------------------- +// onCommitPriority() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onCommitPriority(LLUICtrl* ctrl, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + if (!previewp->getEnabled()) + return; + + LLVOAvatar* avatarp; + if (previewp->mInWorld) + { + if (!gAgentAvatarp) + { + return; + } + avatarp = gAgentAvatarp; + } + else + { + if (!previewp->mAnimPreview) + { + return; + } + avatarp = previewp->mAnimPreview->getDummyAvatar(); + } + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + + motionp->setPriority(llfloor((F32)previewp->childGetValue("priority").asReal())); +} + +//----------------------------------------------------------------------------- +// onCommitEaseIn() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onCommitEaseIn(LLUICtrl* ctrl, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + if (!previewp->getEnabled()) + return; + + LLVOAvatar* avatarp; + if (previewp->mInWorld) + { + if (!gAgentAvatarp) + { + return; + } + avatarp = gAgentAvatarp; + } + else + { + if (!previewp->mAnimPreview) + { + return; + } + avatarp = previewp->mAnimPreview->getDummyAvatar(); + } + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + + motionp->setEaseIn((F32)previewp->childGetValue("ease_in_time").asReal()); + previewp->resetMotion(); +} + +//----------------------------------------------------------------------------- +// onCommitEaseOut() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onCommitEaseOut(LLUICtrl* ctrl, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + if (!previewp->getEnabled()) + return; + + LLVOAvatar* avatarp; + if (previewp->mInWorld) + { + if (!gAgentAvatarp) + { + return; + } + avatarp = gAgentAvatarp; + } + else + { + if (!previewp->mAnimPreview) + { + return; + } + avatarp = previewp->mAnimPreview->getDummyAvatar(); + } + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + + motionp->setEaseOut((F32)previewp->childGetValue("ease_out_time").asReal()); + previewp->resetMotion(); +} + +//----------------------------------------------------------------------------- +// validateEaseIn() +//----------------------------------------------------------------------------- +BOOL LLFloaterBvhPreview::validateEaseIn(LLUICtrl* spin, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + if (!previewp->getEnabled()) + return FALSE; + + LLVOAvatar* avatarp; + if (previewp->mInWorld) + { + if (!gAgentAvatarp) + { + return FALSE; + } + avatarp = gAgentAvatarp; + } + else + { + if (!previewp->mAnimPreview) + { + return FALSE; + } + avatarp = previewp->mAnimPreview->getDummyAvatar(); + } + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + + if (!motionp->getLoop()) + { + F32 new_ease_in = llclamp((F32)previewp->childGetValue("ease_in_time").asReal(), 0.f, motionp->getDuration() - motionp->getEaseOutDuration()); + previewp->childSetValue("ease_in_time", LLSD(new_ease_in)); + } + + return TRUE; +} + +//----------------------------------------------------------------------------- +// validateEaseOut() +//----------------------------------------------------------------------------- +BOOL LLFloaterBvhPreview::validateEaseOut(LLUICtrl* spin, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + + if (!previewp->getEnabled()) + return FALSE; + + LLVOAvatar* avatarp; + if (previewp->mInWorld) + { + if (!gAgentAvatarp) + { + return FALSE; + } + avatarp = gAgentAvatarp; + } + else + { + if (!previewp->mAnimPreview) + { + return FALSE; + } + avatarp = previewp->mAnimPreview->getDummyAvatar(); + } + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + + if (!motionp->getLoop()) + { + F32 new_ease_out = llclamp((F32)previewp->childGetValue("ease_out_time").asReal(), 0.f, motionp->getDuration() - motionp->getEaseInDuration()); + previewp->childSetValue("ease_out_time", LLSD(new_ease_out)); + } + + return TRUE; +} + +//----------------------------------------------------------------------------- +// validateLoopIn() +//----------------------------------------------------------------------------- +BOOL LLFloaterBvhPreview::validateLoopIn(LLUICtrl* ctrl, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + if (!previewp->getEnabled()) + return FALSE; + + F32 loop_in_value = (F32)previewp->childGetValue("loop_in_point").asReal(); + F32 loop_out_value = (F32)previewp->childGetValue("loop_out_point").asReal(); + + if (loop_in_value < 0.f) + { + loop_in_value = 0.f; + } + else if (loop_in_value > 100.f) + { + loop_in_value = 100.f; + } + else if (loop_in_value > loop_out_value) + { + loop_in_value = loop_out_value; + } + + previewp->childSetValue("loop_in_point", LLSD(loop_in_value)); + return TRUE; +} + +//----------------------------------------------------------------------------- +// validateLoopOut() +//----------------------------------------------------------------------------- +BOOL LLFloaterBvhPreview::validateLoopOut(LLUICtrl* spin, void* data) +{ + LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; + if (!previewp->getEnabled()) + return FALSE; + + F32 loop_out_value = (F32)previewp->childGetValue("loop_out_point").asReal(); + F32 loop_in_value = (F32)previewp->childGetValue("loop_in_point").asReal(); + + if (loop_out_value < 0.f) + { + loop_out_value = 0.f; + } + else if (loop_out_value > 100.f) + { + loop_out_value = 100.f; + } + else if (loop_out_value < loop_in_value) + { + loop_out_value = loop_in_value; + } + + previewp->childSetValue("loop_out_point", LLSD(loop_out_value)); + return TRUE; +} + + +//----------------------------------------------------------------------------- +// refresh() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::refresh() +{ + if (!mAnimPreview) + { + childShow("bad_animation_text"); + mPlayButton->setEnabled(FALSE); + mStopButton->setEnabled(FALSE); + childDisable("ok_btn"); + } + else + { + if (!mInWorld) + { + childHide("bad_animation_text"); + } + mPlayButton->setEnabled(TRUE); + LLVOAvatar* avatarp; + if (mInWorld) + { + avatarp = gAgentAvatarp; + } + else + { + avatarp = mAnimPreview->getDummyAvatar(); + } + if (avatarp->isMotionActive(mMotionID)) + { + mStopButton->setEnabled(TRUE); + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(mMotionID); + if (avatarp->areAnimationsPaused()) + { + + mPlayButton->setImages(std::string("button_anim_play.tga"), + std::string("button_anim_play_selected.tga")); + + } + else + { + if (motionp) + { + F32 fraction_complete = motionp->getLastUpdateTime() / motionp->getDuration(); + childSetValue("playback_slider", fraction_complete); + } + mPlayButton->setImages(std::string("button_anim_pause.tga"), + std::string("button_anim_pause_selected.tga")); + + } + } + else + { + mPauseRequest = avatarp->requestPause(); + mPlayButton->setImages(std::string("button_anim_play.tga"), + std::string("button_anim_play_selected.tga")); + + mStopButton->setEnabled(TRUE); // stop also resets, leave enabled. + } + childEnable("ok_btn"); + if (!mInWorld) + { + mAnimPreview->requestUpdate(); + } + } +} + +//----------------------------------------------------------------------------- +// onBtnOK() +//----------------------------------------------------------------------------- +void LLFloaterBvhPreview::onBtnOK(void* userdata) +{ + LLFloaterBvhPreview* floaterp = (LLFloaterBvhPreview*)userdata; + if (!floaterp->getEnabled()) return; + + if ((!floaterp->mInWorld && floaterp->mAnimPreview) || (floaterp->mInWorld && gAgentAvatarp)) + { + LLKeyframeMotion* motionp; + if (floaterp->mInWorld) + { + motionp = (LLKeyframeMotion*)gAgentAvatarp->findMotion(floaterp->mMotionID); + } + else + { + motionp = (LLKeyframeMotion*)floaterp->mAnimPreview->getDummyAvatar()->findMotion(floaterp->mMotionID); + } + + S32 file_size = motionp->getFileSize(); + U8* buffer = new U8[file_size]; + + LLDataPackerBinaryBuffer dp(buffer, file_size); + if (motionp->serialize(dp)) + { + LLVFile file(gVFS, motionp->getID(), LLAssetType::AT_ANIMATION, LLVFile::APPEND); + + S32 size = dp.getCurrentSize(); + file.setMaxSize(size); + if (file.write((U8*)buffer, size)) + { + std::string name = floaterp->childGetValue("name_form").asString(); + std::string desc = floaterp->childGetValue("description_form").asString(); + LLAssetStorage::LLStoreAssetCallback callback = NULL; + S32 expected_upload_cost = sUploadAmount; + void *userdata = NULL; + + // + if(floaterp->mItem) + { + // Update existing item instead of creating a new one + LLViewerInventoryItem* item = (LLViewerInventoryItem*)floaterp->mItem; + LLSaveInfo* info = new LLSaveInfo(item->getUUID(), LLUUID::null, desc, floaterp->mTransactionID); + gAssetStorage->storeAssetData(floaterp->mTransactionID, LLAssetType::AT_ANIMATION, NULL, info, FALSE); + + // I guess I will do this now because the floater is closing... + LLPointer new_item = new LLViewerInventoryItem(item); + new_item->setDescription(desc); + new_item->setTransactionID(floaterp->mTransactionID); + new_item->setAssetUUID(motionp->getID()); + new_item->updateServer(FALSE); + gInventory.updateItem(new_item); + gInventory.notifyObservers(); + } + else + // + { + upload_new_resource(floaterp->mTransactionID, // tid + LLAssetType::AT_ANIMATION, + name, + desc, + 0, + LLFolderType::FT_NONE, + LLInventoryType::IT_ANIMATION, + LLFloaterPerms::getNextOwnerPerms(), LLFloaterPerms::getGroupPerms(), LLFloaterPerms::getEveryonePerms(), + name, + callback, expected_upload_cost, userdata); + } + } + else + { + llwarns << "Failure writing animation data." << llendl; + LLNotificationsUtil::add("WriteAnimationFail"); + } + } + + delete [] buffer; + // clear out cache for motion data + if (floaterp->mInWorld) + { + gAgentAvatarp->removeMotion(floaterp->mMotionID); + gAgentAvatarp->deactivateAllMotions(); + } + else + { + floaterp->mAnimPreview->getDummyAvatar()->removeMotion(floaterp->mMotionID); + } + LLKeyframeDataCache::removeKeyframeData(floaterp->mMotionID); + } + + floaterp->close(false); +} + +//----------------------------------------------------------------------------- +// LLPreviewAnimation +//----------------------------------------------------------------------------- +LLPreviewAnimation::LLPreviewAnimation(S32 width, S32 height) : LLViewerDynamicTexture(width, height, 3, ORDER_MIDDLE, FALSE) +{ + mNeedsUpdate = TRUE; + mCameraDistance = PREVIEW_CAMERA_DISTANCE; + mCameraYaw = 0.f; + mCameraPitch = 0.f; + mCameraZoom = 1.f; + + mDummyAvatar = (LLVOAvatar*)gObjectList.createObjectViewer(LL_PCODE_LEGACY_AVATAR, gAgent.getRegion()); + mDummyAvatar->createDrawable(&gPipeline); + mDummyAvatar->mIsDummy = TRUE; + mDummyAvatar->mSpecialRenderMode = 1; + mDummyAvatar->setPositionAgent(LLVector3::zero); + mDummyAvatar->slamPosition(); + mDummyAvatar->updateJointLODs(); + mDummyAvatar->updateGeometry(mDummyAvatar->mDrawable); + mDummyAvatar->startMotion(ANIM_AGENT_STAND, BASE_ANIM_TIME_OFFSET); + mDummyAvatar->hideSkirt(); + //gPipeline.markVisible(mDummyAvatar->mDrawable, *LLViewerCamera::getInstance()); + + // stop extraneous animations + mDummyAvatar->stopMotion( ANIM_AGENT_HEAD_ROT, TRUE ); + mDummyAvatar->stopMotion( ANIM_AGENT_EYE, TRUE ); + mDummyAvatar->stopMotion( ANIM_AGENT_BODY_NOISE, TRUE ); + mDummyAvatar->stopMotion( ANIM_AGENT_BREATHE_ROT, TRUE ); +} + +//----------------------------------------------------------------------------- +// LLPreviewAnimation() +//----------------------------------------------------------------------------- +LLPreviewAnimation::~LLPreviewAnimation() +{ + mDummyAvatar->markDead(); +} + +//virtual +S8 LLPreviewAnimation::getType() const +{ + return LLViewerDynamicTexture::LL_PREVIEW_ANIMATION ; +} + +//----------------------------------------------------------------------------- +// update() +//----------------------------------------------------------------------------- +BOOL LLPreviewAnimation::render() +{ + mNeedsUpdate = FALSE; + LLVOAvatar* avatarp = mDummyAvatar; + + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.pushMatrix(); + gGL.loadIdentity(); + gGL.ortho(0.0f, mFullWidth, 0.0f, mFullHeight, -1.0f, 1.0f); + + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.pushMatrix(); + gGL.loadIdentity(); + + if (LLGLSLShader::sNoFixedFunction) + { + gUIProgram.bind(); + } + + LLGLSUIDefault def; + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.color4f(0.15f, 0.2f, 0.3f, 1.f); + + gl_rect_2d_simple( mFullWidth, mFullHeight ); + + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.popMatrix(); + + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.popMatrix(); + + gGL.flush(); + + LLVector3 target_pos = avatarp->mRoot->getWorldPosition(); + + LLQuaternion camera_rot = LLQuaternion(mCameraPitch, LLVector3::y_axis) * + LLQuaternion(mCameraYaw, LLVector3::z_axis); + + LLQuaternion av_rot = avatarp->mRoot->getWorldRotation() * camera_rot; + LLViewerCamera::getInstance()->setOriginAndLookAt( + target_pos + ((LLVector3(mCameraDistance, 0.f, 0.f) + mCameraOffset) * av_rot), // camera + LLVector3::z_axis, // up + target_pos + (mCameraOffset * av_rot) ); // point of interest + + LLViewerCamera::getInstance()->setView(LLViewerCamera::getInstance()->getDefaultFOV() / mCameraZoom); + LLViewerCamera::getInstance()->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, mFullWidth, mFullHeight, FALSE); + + mCameraRelPos = LLViewerCamera::getInstance()->getOrigin() - avatarp->mHeadp->getWorldPosition(); + + //avatarp->setAnimationData("LookAtPoint", (void *)&mCameraRelPos); + + //SJB: Animation is updated in LLVOAvatar::updateCharacter + + if (avatarp->mDrawable.notNull()) + { + avatarp->updateLOD(); + + LLVertexBuffer::unbind(); + LLGLDepthTest gls_depth(GL_TRUE); + + LLDrawPoolAvatar *avatarPoolp = (LLDrawPoolAvatar *)avatarp->mDrawable->getFace(0)->getPool(); + avatarp->dirtyMesh(); + avatarPoolp->renderAvatars(avatarp); // renders only one avatar + } + + gGL.color4f(1,1,1,1); + return TRUE; +} + +//----------------------------------------------------------------------------- +// requestUpdate() +//----------------------------------------------------------------------------- +void LLPreviewAnimation::requestUpdate() +{ + mNeedsUpdate = TRUE; +} + +//----------------------------------------------------------------------------- +// rotate() +//----------------------------------------------------------------------------- +void LLPreviewAnimation::rotate(F32 yaw_radians, F32 pitch_radians) +{ + mCameraYaw = mCameraYaw + yaw_radians; + + mCameraPitch = llclamp(mCameraPitch + pitch_radians, F_PI_BY_TWO * -0.8f, F_PI_BY_TWO * 0.8f); +} + +//----------------------------------------------------------------------------- +// zoom() +//----------------------------------------------------------------------------- +void LLPreviewAnimation::zoom(F32 zoom_delta) +{ + setZoom(mCameraZoom + zoom_delta); +} + +//----------------------------------------------------------------------------- +// setZoom() +//----------------------------------------------------------------------------- +void LLPreviewAnimation::setZoom(F32 zoom_amt) +{ + mCameraZoom = llclamp(zoom_amt, MIN_CAMERA_ZOOM, MAX_CAMERA_ZOOM); +} + +//----------------------------------------------------------------------------- +// pan() +//----------------------------------------------------------------------------- +void LLPreviewAnimation::pan(F32 right, F32 up) +{ + mCameraOffset.mV[VY] = llclamp(mCameraOffset.mV[VY] + right * mCameraDistance / mCameraZoom, -1.f, 1.f); + mCameraOffset.mV[VZ] = llclamp(mCameraOffset.mV[VZ] + up * mCameraDistance / mCameraZoom, -1.f, 1.f); +} + + + diff --git a/indra/newview/llfloaterbvhpreview.h b/indra/newview/llfloaterbvhpreview.h new file mode 100644 index 000000000..6a6adaa30 --- /dev/null +++ b/indra/newview/llfloaterbvhpreview.h @@ -0,0 +1,145 @@ +/** + * @file llfloaterbvhpreview.h + * @brief LLFloaterBvhPreview class definition + * + * $LicenseInfo:firstyear=2004&license=viewergpl$ + * + * Copyright (c) 2004-2009, 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://secondlifegrid.net/programs/open_source/licensing/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://secondlifegrid.net/programs/open_source/licensing/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. + * $/LicenseInfo$ + */ + +#ifndef LL_LLFLOATERBVHPREVIEW_H +#define LL_LLFLOATERBVHPREVIEW_H + +#include "llfloaternamedesc.h" +#include "lldynamictexture.h" +#include "llcharacter.h" +#include "llquaternion.h" + +class LLVOAvatar; +class LLViewerJointMesh; + +class LLPreviewAnimation : public LLViewerDynamicTexture +{ +protected: + virtual ~LLPreviewAnimation(); + +public: + LLPreviewAnimation(S32 width, S32 height); + + /*virtual*/ S8 getType() const ; + + BOOL render(); + void requestUpdate(); + void rotate(F32 yaw_radians, F32 pitch_radians); + void zoom(F32 zoom_delta); + void setZoom(F32 zoom_amt); + void pan(F32 right, F32 up); + virtual BOOL needsUpdate() { return mNeedsUpdate; } + + LLVOAvatar* getDummyAvatar() { return mDummyAvatar; } + +protected: + BOOL mNeedsUpdate; + F32 mCameraDistance; + F32 mCameraYaw; + F32 mCameraPitch; + F32 mCameraZoom; + LLVector3 mCameraOffset; + LLVector3 mCameraRelPos; + LLPointer mDummyAvatar; +}; + +class LLFloaterBvhPreview : public LLFloaterNameDesc +{ +public: + // + LLFloaterBvhPreview(const std::string& filename, void* item = NULL); + // + virtual ~LLFloaterBvhPreview(); + + BOOL postBuild(); + + 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); + void onMouseCaptureLost(); + + void refresh(); + + static void onBtnPlay(void*); + static void onBtnStop(void*); + static void setUploadAmount(S32 amount) { sUploadAmount = amount; } + static void onSliderMove(LLUICtrl*, void*); + static void onCommitBaseAnim(LLUICtrl*, void*); + static void onCommitLoop(LLUICtrl*, void*); + static void onCommitLoopIn(LLUICtrl*, void*); + static void onCommitLoopOut(LLUICtrl*, void*); + static BOOL validateLoopIn(LLUICtrl*, void*); + static BOOL validateLoopOut(LLUICtrl*, void*); + static void onCommitName(LLUICtrl*, void*); + static void onCommitHandPose(LLUICtrl*, void*); + static void onCommitEmote(LLUICtrl*, void*); + static void onCommitPriority(LLUICtrl*, void*); + static void onCommitEaseIn(LLUICtrl*, void*); + static void onCommitEaseOut(LLUICtrl*, void*); + static BOOL validateEaseIn(LLUICtrl*, void*); + static BOOL validateEaseOut(LLUICtrl*, void*); + static void onBtnOK(void*); + static void onSaveComplete(const LLUUID& asset_uuid, + LLAssetType::EType type, + void* user_data, + S32 status, LLExtStat ext_status); +private: + void setAnimCallbacks() ; + +protected: + void draw(); + void resetMotion(); + + LLPointer< LLPreviewAnimation> mAnimPreview; + S32 mLastMouseX; + S32 mLastMouseY; + LLButton* mPlayButton; + LLButton* mStopButton; + LLRect mPreviewRect; + LLRectf mPreviewImageRect; + LLAssetID mMotionID; + LLTransactionID mTransactionID; + BOOL mEnabled; + BOOL mInWorld; + LLAnimPauseRequest mPauseRequest; + + std::map mIDList; + + static S32 sUploadAmount; + + // + void* mItem; + // +}; + +#endif // LL_LLFLOATERBVHPREVIEW_H diff --git a/indra/newview/llfloaterexploreanimations.cpp b/indra/newview/llfloaterexploreanimations.cpp index 9a973c8c8..6267b31c5 100644 --- a/indra/newview/llfloaterexploreanimations.cpp +++ b/indra/newview/llfloaterexploreanimations.cpp @@ -4,7 +4,7 @@ #include "llfloaterexploreanimations.h" #include "lluictrlfactory.h" #include "llscrolllistctrl.h" -#include "llfloateranimpreview.h" +#include "llfloaterbvhpreview.h" #include "llvoavatar.h" #include "lllocalinventory.h" #include "llviewercamera.h" diff --git a/indra/newview/llfloaterexploreanimations.h b/indra/newview/llfloaterexploreanimations.h index 27c4af3c1..383b16e6d 100644 --- a/indra/newview/llfloaterexploreanimations.h +++ b/indra/newview/llfloaterexploreanimations.h @@ -3,7 +3,7 @@ #define LL_LLFLOATEREXPLOREANIMATIONS_H #include "llfloater.h" -#include "llfloateranimpreview.h" +#include "llfloaterbvhpreview.h" #include "llviewerwindow.h" // gViewerWindow class LLAnimHistoryItem diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index b18c4b49c..4d9dcd13e 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -63,7 +63,7 @@ #include "llfirstuse.h" #include "llfloaterabout.h" #include "llfloateractivespeakers.h" -#include "llfloateranimpreview.h" +#include "llfloaterbvhpreview.h" #include "llfloateravatarinfo.h" #include "llfloateravatarlist.h" #include "llfloateravatartextures.h" diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index 7eb89b582..5d3f97dc9 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -39,6 +39,7 @@ #include "llagentcamera.h" #include "statemachine/aifilepicker.h" #include "llfloateranimpreview.h" +#include "llfloaterbvhpreview.h" #include "llfloaterimagepreview.h" #include "llfloatermodelpreview.h" #include "llfloaternamedesc.h" @@ -354,8 +355,17 @@ class LLFileUploadAnim : public view_listener_t, public AIFileUpload // Inherited from AIFileUpload. /*virtual*/ void handle_event(std::string const& filename) { - LLFloaterAnimPreview* floaterp = new LLFloaterAnimPreview(filename); - LLUICtrlFactory::getInstance()->buildFloater(floaterp, "floater_animation_preview.xml"); + int len = filename.size(); + if (len >= 5 && filename.substr(len - 5, 5) == ".anim") + { + LLFloaterAnimPreview* floaterp = new LLFloaterAnimPreview(filename); + LLUICtrlFactory::getInstance()->buildFloater(floaterp, "floater_animation_anim_preview.xml"); + floaterp->childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%s%d", gHippoGridManager->getConnectedGrid()->getCurrencySymbol().c_str(), LLGlobalEconomy::Singleton::getInstance()->getPriceUpload())); + } + else + { + LLUICtrlFactory::getInstance()->buildFloater(new LLFloaterBvhPreview(filename), "floater_animation_bvh_preview.xml"); + } } }; @@ -882,8 +892,8 @@ void upload_new_resource(const std::string& src_filename, std::string name, } else if (exten == "bvh") { - error_message = llformat("We do not currently support bulk upload of animation files\n"); - upload_error(error_message, "DoNotSupportBulkAnimationUpload", filename, args); + error_message = llformat("We do not currently support bulk upload of BVH animation files\n"); + upload_error(error_message, "DoNotSupportBulkBVHAnimationUpload", filename, args); return; } // diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 8e879452e..63068994a 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -54,7 +54,7 @@ #include "llagentcamera.h" #include "llcallingcard.h" #include "llfirstuse.h" -#include "llfloateranimpreview.h" +#include "llfloaterbvhpreview.h" #include "llfloaterbump.h" #include "llfloaterbuycurrency.h" #include "llfloaterbuyland.h" @@ -6549,7 +6549,7 @@ void process_economy_data(LLMessageSystem *msg, void** /*user_data*/) LL_INFOS_ONCE("Messaging") << "EconomyData message arrived; upload cost is L$" << upload_cost << LL_ENDL; LLFloaterImagePreview::setUploadAmount(upload_cost); - LLFloaterAnimPreview::setUploadAmount(upload_cost); + LLFloaterBvhPreview::setUploadAmount(upload_cost); std::string fee = gHippoGridManager->getConnectedGrid()->getUploadFee(); gMenuHolder->childSetLabelArg("Upload Image", "[UPLOADFEE]", fee); diff --git a/indra/newview/llviewerobjectbackup.cpp b/indra/newview/llviewerobjectbackup.cpp index 49d2e25bc..91c83f6ba 100644 --- a/indra/newview/llviewerobjectbackup.cpp +++ b/indra/newview/llviewerobjectbackup.cpp @@ -56,7 +56,7 @@ #include "llappviewer.h" #include "llassetuploadresponders.h" #include "statemachine/aifilepicker.h" -#include "llfloateranimpreview.h" +#include "llfloaterbvhpreview.h" #include "llfloaterbuycurrency.h" #include "llfloaterimagepreview.h" #include "llfloaternamedesc.h" diff --git a/indra/newview/skins/default/xui/en-us/floater_animation_anim_preview.xml b/indra/newview/skins/default/xui/en-us/floater_animation_anim_preview.xml new file mode 100644 index 000000000..785a80334 --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/floater_animation_anim_preview.xml @@ -0,0 +1,77 @@ + + + + Name: + + + + Description: + + +