diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 7757ba8f0c15fed751f2383270f74389eb00bac8..bc91c83abe5741122dca7e04b897e9d05bcd4a77 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -296,6 +296,7 @@ set(viewer_SOURCE_FILES llfloaterpathfindingconsole.cpp llfloaterpathfindinglinksets.cpp llfloaterpathfindingobjects.cpp + llfloaterparticleeditor.cpp llfloaterpay.cpp llfloaterperms.cpp llfloaterpostprocess.cpp @@ -929,6 +930,7 @@ set(viewer_HEADER_FILES llfloaterobjectweights.h llfloateropenobject.h llfloateroutbox.h + llfloaterparticleeditor.h llfloaterpathfindingcharacters.h llfloaterpathfindingconsole.h llfloaterpathfindinglinksets.h diff --git a/indra/newview/llfloaterparticleeditor.cpp b/indra/newview/llfloaterparticleeditor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ef190d044cceafb1d526c1b3edeb44b126722b95 --- /dev/null +++ b/indra/newview/llfloaterparticleeditor.cpp @@ -0,0 +1,646 @@ +/** + * @file llfloaterparticleeditor.cpp + * @brief Particle Editor + * + * Copyright (C) 2011, Zi Ree @ Second Life + * Copyright (C) 2015, Cinder Roxley <cinder@sdf.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "llviewerprecompiledheaders.h" +#include "llfloaterparticleeditor.h" + +#include "llagent.h" +#include "llappviewer.h" +#include "llassetuploadresponders.h" +#include "llcheckboxctrl.h" +#include "llclipboard.h" +#include "llcolorswatch.h" +#include "llcombobox.h" +#include "lldir.h" +#include "llfoldertype.h" +#include "llhttpclient.h" +#include "llinventorymodel.h" +#include "llinventorytype.h" +#include "llnotificationsutil.h" +#include "llpermissions.h" +#include "llpreviewscript.h" +#include "llsd.h" +#include "lltexturectrl.h" +#include "lltoolmgr.h" +#include "lltoolobjpicker.h" +#include "llviewerobject.h" +#include "llviewerobjectlist.h" +#include "llviewerpartsim.h" +#include "llviewerpartsource.h" +#include "llviewerregion.h" +#include "llviewertexture.h" +#include "llwindow.h" + +struct lsl_part_st { + U8 flag; + const char* script_const; + lsl_part_st(const U8 f, const char* sc) + { flag = f, script_const = sc; } +}; + +static const std::map<std::string, lsl_part_st> sParticlePatterns{ + {"drop", lsl_part_st(LLPartSysData::LL_PART_SRC_PATTERN_DROP, "PSYS_SRC_PATTERN_DROP")}, + {"explode", lsl_part_st(LLPartSysData::LL_PART_SRC_PATTERN_EXPLODE, "PSYS_SRC_PATTERN_EXPLODE")}, + {"angle", lsl_part_st(LLPartSysData::LL_PART_SRC_PATTERN_ANGLE, "PSYS_SRC_PATTERN_ANGLE")}, + {"angle_cone", lsl_part_st(LLPartSysData::LL_PART_SRC_PATTERN_ANGLE_CONE, "PSYS_SRC_PATTERN_ANGLE_CONE")}, + {"angle_cone_empty", lsl_part_st(LLPartSysData::LL_PART_SRC_PATTERN_ANGLE_CONE_EMPTY, "PSYS_SRC_PATTERN_ANGLE_CONE_EMPTY")} +}; + +static const std::map<std::string, lsl_part_st> sParticleBlends{ + {"blend_one", lsl_part_st(LLPartData::LL_PART_BF_ONE, "PSYS_PART_BF_ONE")}, + {"blend_zero", lsl_part_st(LLPartData::LL_PART_BF_ZERO, "PSYS_PART_BF_ZERO")}, + {"blend_dest_color", lsl_part_st(LLPartData::LL_PART_BF_DEST_COLOR, "PSYS_PART_BF_DEST_COLOR")}, + {"blend_src_color", lsl_part_st(LLPartData::LL_PART_BF_SOURCE_COLOR, "PSYS_PART_BF_SOURCE_COLOR")}, + {"blend_one_minus_dest_color", lsl_part_st(LLPartData::LL_PART_BF_ONE_MINUS_DEST_COLOR, "PSYS_PART_BF_ONE_MINUS_DEST_COLOR")}, + {"blend_one_minus_src_color", lsl_part_st(LLPartData::LL_PART_BF_ONE_MINUS_SOURCE_COLOR, "PSYS_PART_BF_SOURCE_ALPHA")}, + {"blend_src_alpha", lsl_part_st(LLPartData::LL_PART_BF_SOURCE_ALPHA, "PSYS_PART_BF_SOURCE_ALPHA")}, + {"blend_one_minus_src_alpha", lsl_part_st(LLPartData::LL_PART_BF_ONE_MINUS_SOURCE_ALPHA, "PSYS_PART_BF_ONE_MINUS_SOURCE_ALPHA")} +}; + +LLFloaterParticleEditor::LLFloaterParticleEditor(const LLSD& key) +: LLFloater(key), mObject(nullptr), mParticleScriptInventoryItem(nullptr) +, mPatternTypeCombo(nullptr), mTexturePicker(nullptr) +, mBurstRateCtrl(nullptr), mBurstCountCtrl(nullptr), mBurstRadiusCtrl(nullptr) +, mAngleBeginCtrl(nullptr), mAngleEndCtrl(nullptr), mBurstSpeedMinCtrl(nullptr) +, mBurstSpeedMaxCtrl(nullptr), mStartAlphaCtrl(nullptr), mEndAlphaCtrl(nullptr) +, mScaleStartXCtrl(nullptr), mScaleStartYCtrl(nullptr), mScaleEndXCtrl(nullptr) +, mScaleEndYCtrl(nullptr), mSourceMaxAgeCtrl(nullptr), mParticlesMaxAgeCtrl(nullptr) +, mStartGlowCtrl(nullptr), mEndGlowCtrl(nullptr), mBlendFuncSrcCombo(nullptr) +, mBlendFuncDestCombo(nullptr), mBounceCheckBox(nullptr), mEmissiveCheckBox(nullptr) +, mFollowSourceCheckBox(nullptr), mFollowVelocityCheckBox(nullptr), mInterpolateColorCheckBox(nullptr) +, mInterpolateScaleCheckBox(nullptr), mTargetPositionCheckBox(nullptr), mTargetLinearCheckBox(nullptr) +, mWindCheckBox(nullptr), mRibbonCheckBox(nullptr), mAcellerationXCtrl(nullptr) +, mAcellerationYCtrl(nullptr), mAcellerationZCtrl(nullptr), mOmegaXCtrl(nullptr) +, mOmegaYCtrl(nullptr), mOmegaZCtrl(nullptr), mStartColorSelector(nullptr) +, mEndColorSelector(nullptr), mTargetKeyInput(nullptr), mClearTargetButton(nullptr) +, mPickTargetButton(nullptr), mInjectScriptButton(nullptr), mCopyToLSLButton(nullptr) +{ + mCommitCallbackRegistrar.add("Particle.Edit", boost::bind(&LLFloaterParticleEditor::onParameterChange, this)); + mDefaultParticleTexture = LLViewerTextureManager::getFetchedTextureFromFile("pixiesmall.j2c"); +} + +LLFloaterParticleEditor::~LLFloaterParticleEditor() +{ + clearParticles(); +} + +BOOL LLFloaterParticleEditor::postBuild() +{ + LLPanel* panel = getChild<LLPanel>("burst_panel"); + mBurstRateCtrl = panel->getChild<LLUICtrl>("burst_rate"); + mBurstCountCtrl = panel->getChild<LLUICtrl>("burst_count"); + mBurstRadiusCtrl = panel->getChild<LLUICtrl>("burst_radius"); + mBurstSpeedMinCtrl = panel->getChild<LLUICtrl>("burst_speed_min"); + mBurstSpeedMaxCtrl = panel->getChild<LLUICtrl>("burst_speed_max"); + mSourceMaxAgeCtrl = panel->getChild<LLUICtrl>("source_max_age"); + mParticlesMaxAgeCtrl = panel->getChild<LLUICtrl>("particle_max_age"); + + panel = getChild<LLPanel>("angle_panel"); + mAngleBeginCtrl = panel->getChild<LLUICtrl>("angle_begin"); + mAngleEndCtrl = panel->getChild<LLUICtrl>("angle_end"); + mScaleStartXCtrl = panel->getChild<LLUICtrl>("scale_start_x"); + mScaleStartYCtrl = panel->getChild<LLUICtrl>("scale_start_y"); + mScaleEndXCtrl = panel->getChild<LLUICtrl>("scale_end_x"); + mScaleEndYCtrl = panel->getChild<LLUICtrl>("scale_end_y"); + + panel = getChild<LLPanel>("alpha_panel"); + mStartAlphaCtrl = panel->getChild<LLUICtrl>("start_alpha"); + mEndAlphaCtrl = panel->getChild<LLUICtrl>("end_alpha"); + mStartGlowCtrl = panel->getChild<LLUICtrl>("start_glow"); + mEndGlowCtrl = panel->getChild<LLUICtrl>("end_glow"); + + panel = getChild<LLPanel>("omega_panel"); + mAcellerationXCtrl = panel->getChild<LLUICtrl>("acceleration_x"); + mAcellerationYCtrl = panel->getChild<LLUICtrl>("acceleration_y"); + mAcellerationZCtrl = panel->getChild<LLUICtrl>("acceleration_z"); + mOmegaXCtrl = panel->getChild<LLUICtrl>("omega_x"); + mOmegaYCtrl = panel->getChild<LLUICtrl>("omega_y"); + mOmegaZCtrl = panel->getChild<LLUICtrl>("omega_z"); + + panel = getChild<LLPanel>("color_panel"); + mStartColorSelector = panel->getChild<LLColorSwatchCtrl>("start_color_selector"); + mEndColorSelector = panel->getChild<LLColorSwatchCtrl>("end_color_selector"); + mTexturePicker = panel->getChild<LLTextureCtrl>("texture_picker"); + mBlendFuncSrcCombo = panel->getChild<LLComboBox>("blend_func_src_combo"); + mBlendFuncDestCombo = panel->getChild<LLComboBox>("blend_func_dest_combo"); + + panel = getChild<LLPanel>("checkbox_panel"); + mPatternTypeCombo = panel->getChild<LLComboBox>("pattern_type_combo"); + mBounceCheckBox = panel->getChild<LLCheckBoxCtrl>("bounce_checkbox"); + mEmissiveCheckBox = panel->getChild<LLCheckBoxCtrl>("emissive_checkbox"); + mFollowSourceCheckBox = panel->getChild<LLCheckBoxCtrl>("follow_source_checkbox"); + mFollowVelocityCheckBox = panel->getChild<LLCheckBoxCtrl>("follow_velocity_checkbox"); + mInterpolateColorCheckBox = panel->getChild<LLCheckBoxCtrl>("interpolate_color_checkbox"); + mInterpolateScaleCheckBox = panel->getChild<LLCheckBoxCtrl>("interpolate_scale_checkbox"); + mTargetPositionCheckBox = panel->getChild<LLCheckBoxCtrl>("target_position_checkbox"); + mTargetLinearCheckBox = panel->getChild<LLCheckBoxCtrl>("target_linear_checkbox"); + mWindCheckBox = panel->getChild<LLCheckBoxCtrl>("wind_checkbox"); + mRibbonCheckBox = panel->getChild<LLCheckBoxCtrl>("ribbon_checkbox"); + + mTargetKeyInput = panel->getChild<LLUICtrl>("target_key_input"); + mClearTargetButton = panel->getChild<LLButton>("clear_target_button"); + mPickTargetButton = panel->getChild<LLButton>("pick_target_button"); + mInjectScriptButton = panel->getChild<LLButton>("inject_button"); + mCopyToLSLButton = panel->getChild<LLButton>("copy_button"); + + mClearTargetButton->setCommitCallback(boost::bind(&LLFloaterParticleEditor::onClickClearTarget, this)); + mPickTargetButton->setCommitCallback(boost::bind(&LLFloaterParticleEditor::onClickTargetPicker, this)); + mInjectScriptButton->setCommitCallback(boost::bind(&LLFloaterParticleEditor::onClickInject, this)); + mCopyToLSLButton->setCommitCallback(boost::bind(&LLFloaterParticleEditor::onClickCopy, this)); + + mBlendFuncSrcCombo->setValue("blend_src_alpha"); + mBlendFuncDestCombo->setValue("blend_one_minus_src_alpha"); + + onParameterChange(); + + return TRUE; +} + +void LLFloaterParticleEditor::clearParticles() +{ + if (!mObject) return; + + LL_DEBUGS("ParticleEditor") << "clearing particles from " << mObject->getID() << LL_ENDL; + + LLViewerPartSim::getInstance()->clearParticlesByOwnerID(mObject->getID()); +} + +void LLFloaterParticleEditor::updateParticles() +{ + if (!mObject) + return; + + clearParticles(); + LLPointer<LLViewerPartSourceScript> pss = LLViewerPartSourceScript::createPSS(mObject, mParticles); + + pss->setOwnerUUID(mObject->getID()); + pss->setImage(mTexture); + + LLViewerPartSim::getInstance()->addPartSource(pss); +} + +void LLFloaterParticleEditor::setObject(LLViewerObject* objectp) +{ + if (objectp) + { + mObject = objectp; + + LL_DEBUGS("ParticleEditor") << "adding particles to " << mObject->getID() << LL_ENDL; + + updateParticles(); + } +} + +void LLFloaterParticleEditor::onParameterChange() +{ + mParticles.mPattern = sParticlePatterns.at(mPatternTypeCombo->getSelectedValue()).flag; + mParticles.mPartImageID = mTexturePicker->getImageAssetID(); + + // remember the selected texture here to give updateParticles() a UUID to work with + mTexture = LLViewerTextureManager::getFetchedTexture(mTexturePicker->getImageAssetID()); + + if (mTexture->getID() == IMG_DEFAULT || mTexture->getID().isNull()) + { + mTexture = mDefaultParticleTexture; + } + + // limit burst rate to 0.01 to avoid internal freeze, script still gets the real value + mParticles.mBurstRate = llmax<float>(0.01f, mBurstRateCtrl->getValue().asReal()); + mParticles.mBurstPartCount = mBurstCountCtrl->getValue().asInteger(); + mParticles.mBurstRadius = mBurstRadiusCtrl->getValue().asReal(); + mParticles.mInnerAngle = mAngleBeginCtrl->getValue().asReal(); + mParticles.mOuterAngle = mAngleEndCtrl->getValue().asReal(); + mParticles.mBurstSpeedMin = mBurstSpeedMinCtrl->getValue().asReal(); + mParticles.mBurstSpeedMax = mBurstSpeedMaxCtrl->getValue().asReal(); + mParticles.mPartData.setStartAlpha(mStartAlphaCtrl->getValue().asReal()); + mParticles.mPartData.setEndAlpha(mEndAlphaCtrl->getValue().asReal()); + mParticles.mPartData.setStartScale(mScaleStartXCtrl->getValue().asReal(), + mScaleStartYCtrl->getValue().asReal()); + mParticles.mPartData.setEndScale(mScaleEndXCtrl->getValue().asReal(), + mScaleEndYCtrl->getValue().asReal()); + mParticles.mMaxAge = mSourceMaxAgeCtrl->getValue().asReal(); + mParticles.mPartData.setMaxAge(mParticlesMaxAgeCtrl->getValue().asReal()); + + mParticles.mPartData.mStartGlow = mStartGlowCtrl->getValue().asReal(); + mParticles.mPartData.mEndGlow = mEndGlowCtrl->getValue().asReal(); + + mParticles.mPartData.mBlendFuncSource = sParticleBlends.at(mBlendFuncSrcCombo->getSelectedValue()).flag; + mParticles.mPartData.mBlendFuncDest = sParticleBlends.at(mBlendFuncDestCombo->getSelectedValue()).flag; + + U32 flags = 0; + if (mBounceCheckBox->getValue().asBoolean()) flags |= LLPartData::LL_PART_BOUNCE_MASK; + if (mEmissiveCheckBox->getValue().asBoolean()) flags |= LLPartData::LL_PART_EMISSIVE_MASK; + if (mFollowSourceCheckBox->getValue().asBoolean()) flags |= LLPartData::LL_PART_FOLLOW_SRC_MASK; + if (mFollowVelocityCheckBox->getValue().asBoolean()) flags |= LLPartData::LL_PART_FOLLOW_VELOCITY_MASK; + if (mInterpolateColorCheckBox->getValue().asBoolean()) flags |= LLPartData::LL_PART_INTERP_COLOR_MASK; + if (mInterpolateScaleCheckBox->getValue().asBoolean()) flags |= LLPartData::LL_PART_INTERP_SCALE_MASK; + if (mTargetPositionCheckBox->getValue().asBoolean()) flags |= LLPartData::LL_PART_TARGET_POS_MASK; + if (mTargetLinearCheckBox->getValue().asBoolean()) flags |= LLPartData::LL_PART_TARGET_LINEAR_MASK; + if (mWindCheckBox->getValue().asBoolean()) flags |= LLPartData::LL_PART_WIND_MASK; + if (mRibbonCheckBox->getValue().asBoolean()) flags |= LLPartData::LL_PART_RIBBON_MASK; + mParticles.mPartData.setFlags(flags); + + mParticles.mTargetUUID = mTargetKeyInput->getValue().asUUID(); + + mParticles.mPartAccel = LLVector3(mAcellerationXCtrl->getValue().asReal(), + mAcellerationYCtrl->getValue().asReal(), + mAcellerationZCtrl->getValue().asReal()); + mParticles.mAngularVelocity = LLVector3(mOmegaXCtrl->getValue().asReal(), + mOmegaYCtrl->getValue().asReal(), + mOmegaZCtrl->getValue().asReal()); + + LLColor4 color = mStartColorSelector->get(); + mParticles.mPartData.setStartColor(LLVector3(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE])); + color = mEndColorSelector->get(); + mParticles.mPartData.setEndColor(LLVector3(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE])); + + updateUI(); + updateParticles(); +} + +void LLFloaterParticleEditor::updateUI() +{ + U8 pattern = sParticlePatterns.at(mPatternTypeCombo->getValue()).flag; + BOOL drop_pattern = (pattern == LLPartSysData::LL_PART_SRC_PATTERN_DROP); + BOOL explode_pattern = (pattern == LLPartSysData::LL_PART_SRC_PATTERN_EXPLODE); + BOOL target_linear = mTargetLinearCheckBox->getValue(); + BOOL interpolate_color = mInterpolateColorCheckBox->getValue(); + BOOL interpolate_scale = mInterpolateScaleCheckBox->getValue(); + BOOL target_enabled = target_linear | (mTargetPositionCheckBox->getValue().asBoolean() ? TRUE : FALSE); + + mBurstRadiusCtrl->setEnabled(!(target_linear | (mFollowSourceCheckBox->getValue().asBoolean() ? TRUE : FALSE) | drop_pattern)); + mBurstSpeedMinCtrl->setEnabled(!(target_linear | drop_pattern)); + mBurstSpeedMaxCtrl->setEnabled(!(target_linear | drop_pattern)); + + // disabling a color swatch does nothing visually, so we also set alpha + LLColor4 end_color = mEndColorSelector->get(); + end_color.setAlpha(interpolate_color ? 1.0f : 0.0f); + + mEndAlphaCtrl->setEnabled(interpolate_color); + mEndColorSelector->set(end_color); + mEndColorSelector->setEnabled(interpolate_color); + + mScaleEndXCtrl->setEnabled(interpolate_scale); + mScaleEndYCtrl->setEnabled(interpolate_scale); + + mTargetPositionCheckBox->setEnabled(!target_linear); + mTargetKeyInput->setEnabled(target_enabled); + mPickTargetButton->setEnabled(target_enabled); + mClearTargetButton->setEnabled(target_enabled); + + mAcellerationXCtrl->setEnabled(!target_linear); + mAcellerationYCtrl->setEnabled(!target_linear); + mAcellerationZCtrl->setEnabled(!target_linear); + + mOmegaXCtrl->setEnabled(!target_linear); + mOmegaYCtrl->setEnabled(!target_linear); + mOmegaZCtrl->setEnabled(!target_linear); + + mAngleBeginCtrl->setEnabled(!(explode_pattern | drop_pattern)); + mAngleEndCtrl->setEnabled(!(explode_pattern | drop_pattern)); +} + +void LLFloaterParticleEditor::onClickClearTarget() +{ + mTargetKeyInput->clear(); + onParameterChange(); +} + +void LLFloaterParticleEditor::onClickTargetPicker() +{ + mPickTargetButton->setToggleState(TRUE); + mPickTargetButton->setEnabled(FALSE); + startPicking(this); +} + +// static +void LLFloaterParticleEditor::startPicking(void* userdata) +{ + LLFloaterParticleEditor* self = static_cast<LLFloaterParticleEditor*>(userdata); + LLToolObjPicker::getInstance()->setExitCallback(LLFloaterParticleEditor::onTargetPicked, self); + LLToolMgr::getInstance()->setTransientTool(LLToolObjPicker::getInstance()); +} + +// static +void LLFloaterParticleEditor::onTargetPicked(void* userdata) +{ + LLFloaterParticleEditor* self = static_cast<LLFloaterParticleEditor*>(userdata); + + const LLUUID& picked = LLToolObjPicker::getInstance()->getObjectID(); + + LLToolMgr::getInstance()->clearTransientTool(); + + self->mPickTargetButton->setEnabled(TRUE); + self->mPickTargetButton->setToggleState(FALSE); + + if (picked.notNull()) + { + self->mTargetKeyInput->setValue(picked.asString()); + self->onParameterChange(); + } +} + +std::string LLFloaterParticleEditor::lslVector(F32 x, F32 y, F32 z) +{ + return llformat("<%f,%f,%f>", x, y, z); +} + +std::string LLFloaterParticleEditor::lslColor(const LLColor4& color) +{ + return lslVector(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE]); +} + +std::string LLFloaterParticleEditor::createScript() +{ + std::string script( +"\ +default\n\ +{\n\ + state_entry()\n\ + {\n\ + llParticleSystem(\n\ + [\n\ + PSYS_SRC_PATTERN,[PATTERN],\n\ + PSYS_SRC_BURST_RADIUS,[BURST_RADIUS],\n\ + PSYS_SRC_ANGLE_BEGIN,[ANGLE_BEGIN],\n\ + PSYS_SRC_ANGLE_END,[ANGLE_END],\n\ + PSYS_SRC_TARGET_KEY,[TARGET_KEY],\n\ + PSYS_PART_START_COLOR,[START_COLOR],\n\ + PSYS_PART_END_COLOR,[END_COLOR],\n\ + PSYS_PART_START_ALPHA,[START_ALPHA],\n\ + PSYS_PART_END_ALPHA,[END_ALPHA],\n\ + PSYS_PART_START_GLOW,[START_GLOW],\n\ + PSYS_PART_END_GLOW,[END_GLOW],\n\ + PSYS_PART_BLEND_FUNC_SOURCE,[BLEND_FUNC_SOURCE],\n\ + PSYS_PART_BLEND_FUNC_DEST,[BLEND_FUNC_DEST],\n\ + PSYS_PART_START_SCALE,[START_SCALE],\n\ + PSYS_PART_END_SCALE,[END_SCALE],\n\ + PSYS_SRC_TEXTURE,\"[TEXTURE]\",\n\ + PSYS_SRC_MAX_AGE,[SOURCE_MAX_AGE],\n\ + PSYS_PART_MAX_AGE,[PART_MAX_AGE],\n\ + PSYS_SRC_BURST_RATE,[BURST_RATE],\n\ + PSYS_SRC_BURST_PART_COUNT,[BURST_COUNT],\n\ + PSYS_SRC_ACCEL,[ACCELERATION],\n\ + PSYS_SRC_OMEGA,[OMEGA],\n\ + PSYS_SRC_BURST_SPEED_MIN,[BURST_SPEED_MIN],\n\ + PSYS_SRC_BURST_SPEED_MAX,[BURST_SPEED_MAX],\n\ + PSYS_PART_FLAGS,\n\ + 0[FLAGS]\n\ + ]);\n\ + }\n\ +}\n"); + + const LLUUID& targetKey = mTargetKeyInput->getValue().asUUID(); + std::string keyString = "llGetKey()"; + + if (targetKey.notNull() && targetKey != mObject->getID()) + { + keyString="(key) \"" + targetKey.asString() + "\""; + } + + LLUUID textureKey = mTexture->getID(); + std::string textureString; + if (textureKey.notNull() && textureKey != IMG_DEFAULT && textureKey != mDefaultParticleTexture->getID()) + { + textureString = textureKey.asString(); + } + + LLStringUtil::replaceString(script,"[PATTERN]", sParticlePatterns.at(mPatternTypeCombo->getValue()).script_const); + LLStringUtil::replaceString(script,"[BURST_RADIUS]", mBurstRadiusCtrl->getValue().asString()); + LLStringUtil::replaceString(script,"[ANGLE_BEGIN]", mAngleBeginCtrl->getValue().asString()); + LLStringUtil::replaceString(script,"[ANGLE_END]", mAngleEndCtrl->getValue().asString()); + LLStringUtil::replaceString(script,"[TARGET_KEY]", keyString); + LLStringUtil::replaceString(script,"[START_COLOR]", lslColor(mStartColorSelector->get())); + LLStringUtil::replaceString(script,"[END_COLOR]", lslColor(mEndColorSelector->get())); + LLStringUtil::replaceString(script,"[START_ALPHA]", mStartAlphaCtrl->getValue().asString()); + LLStringUtil::replaceString(script,"[END_ALPHA]", mEndAlphaCtrl->getValue().asString()); + LLStringUtil::replaceString(script,"[START_GLOW]", mStartGlowCtrl->getValue().asString()); + LLStringUtil::replaceString(script,"[END_GLOW]", mEndGlowCtrl->getValue().asString()); + LLStringUtil::replaceString(script,"[START_SCALE]", lslVector(mScaleStartXCtrl->getValue().asReal(), + mScaleStartYCtrl->getValue().asReal(), + 0.0f)); + LLStringUtil::replaceString(script,"[END_SCALE]", lslVector(mScaleEndXCtrl->getValue().asReal(), + mScaleEndYCtrl->getValue().asReal(), + 0.0f)); + LLStringUtil::replaceString(script,"[TEXTURE]", textureString); + LLStringUtil::replaceString(script,"[SOURCE_MAX_AGE]", mSourceMaxAgeCtrl->getValue().asString()); + LLStringUtil::replaceString(script,"[PART_MAX_AGE]", mParticlesMaxAgeCtrl->getValue().asString()); + LLStringUtil::replaceString(script,"[BURST_RATE]", mBurstRateCtrl->getValue().asString()); + LLStringUtil::replaceString(script,"[BURST_COUNT]", mBurstCountCtrl->getValue()); + LLStringUtil::replaceString(script,"[ACCELERATION]", lslVector(mAcellerationXCtrl->getValue().asReal(), + mAcellerationYCtrl->getValue().asReal(), + mAcellerationZCtrl->getValue().asReal())); + LLStringUtil::replaceString(script,"[OMEGA]", lslVector(mOmegaXCtrl->getValue().asReal(), + mOmegaYCtrl->getValue().asReal(), + mOmegaZCtrl->getValue().asReal())); + LLStringUtil::replaceString(script,"[BURST_SPEED_MIN]", mBurstSpeedMinCtrl->getValue().asString()); + LLStringUtil::replaceString(script,"[BURST_SPEED_MAX]", mBurstSpeedMaxCtrl->getValue().asString()); + LLStringUtil::replaceString(script,"[BLEND_FUNC_SOURCE]", sParticleBlends.at(mBlendFuncSrcCombo->getValue().asString()).script_const); + LLStringUtil::replaceString(script,"[BLEND_FUNC_DEST]", sParticleBlends.at(mBlendFuncDestCombo->getValue().asString()).script_const); + + std::string delimiter = " |\n "; + std::string flagsString; + + if (mBounceCheckBox->getValue()) + flagsString += delimiter + "PSYS_PART_BOUNCE_MASK"; + if (mEmissiveCheckBox->getValue()) + flagsString += delimiter + "PSYS_PART_EMISSIVE_MASK"; + if (mFollowSourceCheckBox->getValue()) + flagsString += delimiter + "PSYS_PART_FOLLOW_SRC_MASK"; + if (mFollowVelocityCheckBox->getValue()) + flagsString += delimiter + "PSYS_PART_FOLLOW_VELOCITY_MASK"; + if (mInterpolateColorCheckBox->getValue()) + flagsString += delimiter + "PSYS_PART_INTERP_COLOR_MASK"; + if (mInterpolateScaleCheckBox->getValue()) + flagsString += delimiter + "PSYS_PART_INTERP_SCALE_MASK"; + if (mTargetLinearCheckBox->getValue()) + flagsString += delimiter + "PSYS_PART_TARGET_LINEAR_MASK"; + if (mTargetPositionCheckBox->getValue()) + flagsString += delimiter + "PSYS_PART_TARGET_POS_MASK"; + if (mWindCheckBox->getValue()) + flagsString += delimiter + "PSYS_PART_WIND_MASK"; + if (mRibbonCheckBox->getValue()) + flagsString += delimiter + "PSYS_PART_RIBBON_MASK"; + + LLStringUtil::replaceString(script, "[FLAGS]", flagsString); + LL_DEBUGS("ParticleEditor") << "\n" << script << LL_ENDL; + + return script; +} + +void LLFloaterParticleEditor::onClickCopy() +{ + const std::string& script = createScript(); + if (!script.empty()) + { + getWindow()->copyTextToClipboard(utf8str_to_wstring(script)); + LLNotificationsUtil::add("ParticleScriptCopiedToClipboard"); + } +} + +void LLFloaterParticleEditor::onClickInject() +{ + const LLUUID& categoryID = gInventory.findCategoryUUIDForType(LLFolderType::FT_LSL_TEXT); + + // if no valid folder found bail out and complain + if (categoryID.isNull()) + { + LLNotificationsUtil::add("ParticleScriptFindFolderFailed"); + return; + } + + // setup permissions + LLPermissions perm; + perm.init(gAgentID, gAgentID, LLUUID::null, LLUUID::null); + perm.initMasks(PERM_ALL, PERM_ALL, PERM_ALL, PERM_ALL, PERM_ALL); + + // create new script inventory item and wait for it to come back (callback) + LLPointer<LLInventoryCallback> callback = new LLParticleScriptCreationCallback(this); + create_inventory_item( + gAgentID, + gAgentSessionID, + categoryID, + LLTransactionID::tnull, + PARTICLE_SCRIPT_NAME, + LLStringUtil::null, + LLAssetType::AT_LSL_TEXT, + LLInventoryType::IT_LSL, + NOT_WEARABLE, + perm.getMaskNextOwner(), + callback); + + setCanClose(FALSE); +} + +void LLFloaterParticleEditor::callbackReturned(const LLUUID& inventoryItemID) +{ + setCanClose(TRUE); + + if (inventoryItemID.isNull()) + { + LLNotificationsUtil::add("ParticleScriptCreationFailed"); + return; + } + + mParticleScriptInventoryItem = gInventory.getItem(inventoryItemID); + if (!mParticleScriptInventoryItem) + { + LLNotificationsUtil::add("ParticleScriptNotFound"); + return; + } + gInventory.updateItem(mParticleScriptInventoryItem); + gInventory.notifyObservers(); + + //caps import + const std::string& url = gAgent.getRegion()->getCapability("UpdateScriptAgent"); + + if (!url.empty()) + { + const std::string& script = createScript(); + + const std::string& temp_file_path(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "particle_script.lsltxt")); + + std::ofstream temp_file; + + temp_file.open(temp_file_path.c_str()); + if (!temp_file.is_open()) + { + LLNotificationsUtil::add("ParticleScriptCreateTempFileFailed"); + return; + } + + temp_file << script.c_str(); + temp_file.close(); + + LLSD body; + body["task_id"] = mObject->getID(); // probably has no effect + body["item_id"] = inventoryItemID; + body["target"] = "mono"; + body["is_script_running"] = true; + + // responder will alert us when the job is done + LLHTTPClient::post(url, body, new LLParticleScriptUploadResponder(body, temp_file_path, LLAssetType::AT_LSL_TEXT, this)); + + //mMainPanel->setEnabled(FALSE); + setCanClose(FALSE); + } + else + { + LLNotificationsUtil::add("ParticleScriptCapsFailed"); + return; + } +} + +void LLFloaterParticleEditor::scriptInjectReturned(const LLSD& content) +{ + setCanClose(TRUE); + + // play it safe, because some time may have passed + LLViewerObject* object = gObjectList.findObject(mObject->getID()); + if (!object) + { + LL_DEBUGS("ParticleEditor") << "Can't find " << mObject->getID().asString() << " anymore!" << LL_ENDL; + //mMainPanel->setEnabled(TRUE); + return; + } + + mObject->saveScript(mParticleScriptInventoryItem, TRUE, FALSE); + LLNotificationsUtil::add("ParticleScriptInjected"); + + delete this; +} + +// ---------------------------------- Callbacks ---------------------------------- + +LLFloaterParticleEditor::LLParticleScriptCreationCallback:: +LLParticleScriptCreationCallback(LLFloaterParticleEditor* editor) +: mEditor(editor) +{ +} + +void LLFloaterParticleEditor::LLParticleScriptCreationCallback::fire(const LLUUID& inventoryItem) +{ + if (!gDisconnected && !LLAppViewer::instance()->quitRequested() && mEditor) + { + mEditor->callbackReturned(inventoryItem); + } +} + +// ---------------------------------- Responders ---------------------------------- + +LLFloaterParticleEditor::LLParticleScriptUploadResponder:: + LLParticleScriptUploadResponder(const LLSD& post_data, + const std::string& file_name, + LLAssetType::EType asset_type, + LLFloaterParticleEditor* editor) +: LLUpdateAgentInventoryResponder(post_data, file_name, asset_type) +, mEditor(editor) +{} + +void LLFloaterParticleEditor::LLParticleScriptUploadResponder::uploadComplete(const LLSD& content) +{ + LLUpdateAgentInventoryResponder::uploadComplete(content); + if (!gDisconnected && !LLAppViewer::instance()->quitRequested() && mEditor) + { + mEditor->scriptInjectReturned(content); + } +} diff --git a/indra/newview/llfloaterparticleeditor.h b/indra/newview/llfloaterparticleeditor.h new file mode 100644 index 0000000000000000000000000000000000000000..242c87302ce453b721e03c7a81315e5ae1d9b77b --- /dev/null +++ b/indra/newview/llfloaterparticleeditor.h @@ -0,0 +1,161 @@ +/** + * @file llfloaterparticleeditor.h + * @brief Particle Editor + * + * Copyright (C) 2011, Zi Ree @ Second Life + * Copyright (C) 2015, Cinder Roxley <cinder@sdf.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LL_FLOATERPARTICLEEDITOR_H +#define LL_FLOATERPARTICLEEDITOR_H + +#include "llfloater.h" +#include "llpartdata.h" +#include "llviewerinventory.h" +#include "llassetuploadresponders.h" + +class LLButton; +class LLCheckBoxCtrl; +class LLColorSwatchCtrl; +class LLComboBox; +class LLTextureCtrl; +class LLUICtrl; +class LLViewerObject; +class LLViewerTexture; + +const std::string PARTICLE_SCRIPT_NAME = "New Particle Script"; + +class LLFloaterParticleEditor : public LLFloater +{ +public: + LLFloaterParticleEditor(const LLSD& key); + /* virtual */ ~LLFloaterParticleEditor(); + + /* virtual */ BOOL postBuild(); + + void setObject(LLViewerObject* objectp); + +private: + void clearParticles(); + void updateParticles(); + void updateUI(); + + std::string createScript(); + + void onParameterChange(); + void onClickCopy(); + void onClickInject(); + + void onClickClearTarget(); + void onClickTargetPicker(); + static void startPicking(void* userdata); + static void onTargetPicked(void* userdata); + + void callbackReturned(const LLUUID& inv_item); + void scriptInjectReturned(const LLSD& content); + + std::string lslVector(F32 x, F32 y, F32 z); + std::string lslColor(const LLColor4& color); + + LLViewerObject* mObject; + LLViewerTexture* mTexture; + LLViewerInventoryItem* mParticleScriptInventoryItem; + + LLViewerTexture* mDefaultParticleTexture; + + LLPartSysData mParticles; + + LLComboBox* mPatternTypeCombo; + LLTextureCtrl* mTexturePicker; + + LLUICtrl* mBurstRateCtrl; + LLUICtrl* mBurstCountCtrl; + LLUICtrl* mBurstRadiusCtrl; + LLUICtrl* mAngleBeginCtrl; + LLUICtrl* mAngleEndCtrl; + LLUICtrl* mBurstSpeedMinCtrl; + LLUICtrl* mBurstSpeedMaxCtrl; + LLUICtrl* mStartAlphaCtrl; + LLUICtrl* mEndAlphaCtrl; + LLUICtrl* mScaleStartXCtrl; + LLUICtrl* mScaleStartYCtrl; + LLUICtrl* mScaleEndXCtrl; + LLUICtrl* mScaleEndYCtrl; + LLUICtrl* mSourceMaxAgeCtrl; + LLUICtrl* mParticlesMaxAgeCtrl; + LLUICtrl* mStartGlowCtrl; + LLUICtrl* mEndGlowCtrl; + + LLUICtrl* mAcellerationXCtrl; + LLUICtrl* mAcellerationYCtrl; + LLUICtrl* mAcellerationZCtrl; + + LLUICtrl* mOmegaXCtrl; + LLUICtrl* mOmegaYCtrl; + LLUICtrl* mOmegaZCtrl; + + LLComboBox* mBlendFuncSrcCombo; + LLComboBox* mBlendFuncDestCombo; + + LLCheckBoxCtrl* mBounceCheckBox; + LLCheckBoxCtrl* mEmissiveCheckBox; + LLCheckBoxCtrl* mFollowSourceCheckBox; + LLCheckBoxCtrl* mFollowVelocityCheckBox; + LLCheckBoxCtrl* mInterpolateColorCheckBox; + LLCheckBoxCtrl* mInterpolateScaleCheckBox; + LLCheckBoxCtrl* mTargetPositionCheckBox; + LLCheckBoxCtrl* mTargetLinearCheckBox; + LLCheckBoxCtrl* mWindCheckBox; + LLCheckBoxCtrl* mRibbonCheckBox; + + LLUICtrl* mTargetKeyInput; + LLButton* mClearTargetButton; + LLButton* mPickTargetButton; + LLButton* mCopyToLSLButton; + LLButton* mInjectScriptButton; + + LLColorSwatchCtrl* mStartColorSelector; + LLColorSwatchCtrl* mEndColorSelector; + + class LLParticleScriptCreationCallback : public LLInventoryCallback + { + public: + LLParticleScriptCreationCallback(LLFloaterParticleEditor* editor); + void fire(const LLUUID& inventoryItem); + + protected: + ~LLParticleScriptCreationCallback() {} + + LLFloaterParticleEditor* mEditor; + }; + + class LLParticleScriptUploadResponder : public LLUpdateAgentInventoryResponder + { + public: + LLParticleScriptUploadResponder(const LLSD& post_data, + const std::string& file_name, + LLAssetType::EType asset_type, + LLFloaterParticleEditor* editor); + + protected: + void uploadComplete(const LLSD& content); + + LLFloaterParticleEditor* mEditor; + }; +}; + +#endif // LL_FLOATERPARTICLEEDITOR_H diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index b6a736332faaa62095a4d961da05f3bb70b5320c..1d959dcf85001ed7f6ca95c7bce82b984394e3cc 100755 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -93,6 +93,7 @@ #include "llfloaterobjectweights.h" #include "llfloateropenobject.h" #include "llfloateroutbox.h" +#include "llfloaterparticleeditor.h" #include "llfloaterpathfindingcharacters.h" #include "llfloaterpathfindingconsole.h" #include "llfloaterpathfindinglinksets.h" @@ -277,6 +278,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("outgoing_call", "floater_outgoing_call.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLOutgoingCallDialog>); LLFloaterPayUtil::registerFloater(); + LLFloaterReg::add("particle_editor","floater_particle_editor.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterParticleEditor>); LLFloaterReg::add("pathfinding_characters", "floater_pathfinding_characters.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPathfindingCharacters>); LLFloaterReg::add("pathfinding_linksets", "floater_pathfinding_linksets.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPathfindingLinksets>); LLFloaterReg::add("pathfinding_console", "floater_pathfinding_console.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPathfindingConsole>); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index e1295cd96577acf84f71b9ea5d90c1d63b3a50e5..ed52f5d2326bb3e11f5c8823e77a15f82e8e990d 100755 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -67,6 +67,7 @@ #include "llfloaterimcontainer.h" #include "llfloaterland.h" #include "llfloaterimnearbychat.h" +#include "llfloaterparticleeditor.h" #include "llfloaterpathfindingcharacters.h" #include "llfloaterpathfindinglinksets.h" #include "llfloaterpay.h" @@ -3174,6 +3175,37 @@ class LLObjectMute : public view_listener_t }; // <Alchemy> +class LLEnableEditParticleSource : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + if (LLSelectMgr::instance().getSelection()->getObjectCount()) + { + LLObjectSelection::valid_root_iterator iter = LLSelectMgr::instance().getSelection()->valid_root_begin(); + LLSelectNode* node = *iter; + + if (node->mPermissions->getOwner() == gAgent.getID()) + return true; + } + return false; + } +}; + +class LLEditParticleSource : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + if (objectp) + { + LLFloaterParticleEditor* particleEditor = LLFloaterReg::showTypedInstance<LLFloaterParticleEditor>("particle_editor", LLSD(objectp->getID()), TAKE_FOCUS_YES); + if(particleEditor) + particleEditor->setObject(objectp); + } + return true; + } +}; + class LLSyncAnimations : public view_listener_t { bool handleEvent(const LLSD& userdata) @@ -9208,6 +9240,8 @@ void initialize_menus() view_listener_t::addMenu(new LLEditableSelectedMono(), "EditableSelectedMono"); view_listener_t::addMenu(new LLToggleUIHints(), "ToggleUIHints"); // <Alchemy> + view_listener_t::addMenu(new LLEditParticleSource(), "Object.EditParticles"); + view_listener_t::addMenu(new LLEnableEditParticleSource(), "Object.EnableEditParticles"); view_listener_t::addMenu(new LLSyncAnimations(), "Tools.ResyncAnimations"); view_listener_t::addMenu(new ALMarkViewerEffectsDead(), "Tools.AllVEDead"); // </Alchemy> diff --git a/indra/newview/skins/default/xui/en/floater_particle_editor.xml b/indra/newview/skins/default/xui/en/floater_particle_editor.xml new file mode 100644 index 0000000000000000000000000000000000000000..d6ec367c093c7d58961ce927ec19a3d10d8384a1 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_particle_editor.xml @@ -0,0 +1,876 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater + can_resize="true" + height="357" + min_height="359" + min_width="450" + name="particle_editor_floater" + save_rect="true" + save_visibility="false" + single_instance="false" + title="PARTICLE EDITOR" + width="450"> + <layout_stack + follows="all" + animate="false" + top="0" + height="357" + left="0" + right="-1" + drag_handle_gap="0" + drag_handle_first_indent="1" + drag_handle_second_indent="1" + layout="topleft" + name="master_stack" + orientation="horizontal" + show_drag_handle="true"> + <layout_panel + bg_alpha_color="DkGray2" + bg_opaque_color="DkGray2" + background_visible="true" + background_opaque="true" + name="layout_master" + follows="all" + layout="topleft" + auto_resize="false" + user_resize="false" + height="357" + width="280"> + <accordion + left="0" + top="0" + single_expansion="false" + follows="all" + layout="topleft" + name="accordion_master" + height="357" + width="275"> + <accordion_tab + expanded="true" + layout="topleft" + height="152" + name="burst_tab" + title="Burst/Age" + fit_panel="true"> + <panel + follows="all" + height="152" + left="0" + name="burst_panel" + top="0" + right="-1"> + <slider + follows="left|top" + decimal_digits="2" + initial_value="0.3" + increment="0.1" + name="burst_rate" + label="Rate:" + label_width="50" + can_edit_text="true" + left="0" + width="275" + min_val="0" + height="16" + max_val="1000" + top="0"> + <slider.commit_callback + function="Particle.Edit" /> + </slider> + <slider + follows="left|top" + decimal_digits="0" + initial_value="5" + increment="1" + name="burst_count" + label="Count:" + label_width="50" + can_edit_text="true" + left="0" + width="275" + min_val="1" + height="16" + max_val="1000" + top_pad="2"> + <slider.commit_callback + function="Particle.Edit" /> + </slider> + <slider + follows="left|top" + decimal_digits="3" + initial_value="0" + increment="0.001" + name="burst_radius" + label="Radius:" + label_width="50" + can_edit_text="true" + left="0" + width="275" + min_val="0" + max_val="50" + height="16" + top_pad="2"> + <slider.commit_callback + function="Particle.Edit" /> + </slider> + <slider + follows="left|top" + decimal_digits="3" + initial_value="0.5" + increment="0.001" + name="burst_speed_max" + label="Speed Max:" + label_width="70" + can_edit_text="true" + left="0" + width="275" + min_val="0" + max_val="100" + height="16" + top_pad="2"> + <slider.commit_callback + function="Particle.Edit" /> + </slider> + <slider + follows="left|top" + decimal_digits="3" + initial_value="0.5" + increment="0.001" + name="burst_speed_min" + label="Speed Min:" + label_width="70" + can_edit_text="true" + left="0" + width="275" + min_val="0" + max_val="100" + height="16" + top_pad="2"> + <slider.commit_callback + function="Particle.Edit" /> + </slider> + <slider + follows="left|top" + decimal_digits="3" + initial_value="0" + increment="0.001" + name="source_max_age" + label="Source Max Age:" + label_width="100" + can_edit_text="true" + left="0" + width="275" + min_val="0" + max_val="1000" + height="16" + top_pad="2"> + <slider.commit_callback + function="Particle.Edit" /> + </slider> + <slider + follows="left|top" + decimal_digits="3" + initial_value="5" + increment="0.001" + name="particle_max_age" + label="Particle Max Age:" + label_width="100" + can_edit_text="true" + left="0" + width="275" + min_val="0" + max_val="30" + height="16" + top_pad="2"> + <slider.commit_callback + function="Particle.Edit" /> + </slider> + </panel> + </accordion_tab> + <accordion_tab + expanded="false" + layout="topleft" + height="140" + name="angle_tab" + title="Scale/Angle" + fit_panel="true"> + <panel + follows="all" + height="140" + left="0" + name="angle_panel" + top="0" + width="313"> + <slider + follows="left|top" + decimal_digits="3" + initial_value="0.5" + increment="0.001" + name="scale_start_x" + label="Scale Start X:" + label_width="72" + can_edit_text="true" + left="0" + width="275" + min_val="0.03125" + max_val="4" + height="16" + top_pad="2"> + <slider.commit_callback + function="Particle.Edit" /> + </slider> + <slider + follows="left|top" + decimal_digits="3" + initial_value="0.5" + increment="0.001" + name="scale_start_y" + label="Scale Start Y:" + label_width="72" + can_edit_text="true" + left="0" + width="275" + min_val="0.03125" + max_val="4" + height="16" + top_pad="2"> + <slider.commit_callback + function="Particle.Edit" /> + </slider> + <slider + follows="left|top" + decimal_digits="3" + initial_value="0.5" + increment="0.001" + name="scale_end_x" + label="Scale End Y:" + label_width="70" + can_edit_text="true" + left="0" + width="275" + min_val="0.03125" + max_val="4" + height="16" + top_pad="2"> + <slider.commit_callback + function="Particle.Edit" /> + </slider> + <slider + follows="left|top" + decimal_digits="3" + initial_value="0.5" + increment="0.001" + name="scale_end_y" + label="Scale End Y:" + label_width="70" + can_edit_text="true" + left="0" + width="275" + min_val="0.03125" + max_val="4" + height="16" + top_pad="2"> + <slider.commit_callback + function="Particle.Edit" /> + </slider> + <slider + follows="left|top" + decimal_digits="3" + initial_value="0" + increment="0.001" + name="angle_begin" + label="Angle Begin:" + label_width="70" + can_edit_text="true" + left="0" + width="275" + min_val="-3.14" + max_val="3.14" + height="16" + top_pad="2"> + <slider.commit_callback + function="Particle.Edit" /> + </slider> + <slider + follows="left|top" + decimal_digits="3" + initial_value="0" + increment="0.001" + name="angle_end" + label="Angle End:" + label_width="70" + can_edit_text="true" + left="0" + width="275" + min_val="-3.14" + max_val="3.14" + height="16" + top_pad="2"> + <slider.commit_callback + function="Particle.Edit" /> + </slider> + </panel> + </accordion_tab> + <accordion_tab + expanded="false" + layout="topleft" + height="100" + name="alpha_tab" + title="Alpha/Glow" + fit_panel="true"> + <panel + follows="all" + height="100" + left="0" + name="alpha_panel" + top="0" + width="313"> + <slider + follows="left|top" + decimal_digits="3" + initial_value="1" + increment="0.001" + name="start_alpha" + label="Start Alpha:" + label_width="70" + can_edit_text="true" + left="0" + width="275" + min_val="0" + max_val="1" + height="16" + top_pad="2"> + <slider.commit_callback + function="Particle.Edit" /> + </slider> + <slider + follows="left|top" + decimal_digits="3" + initial_value="1" + increment="0.001" + name="end_alpha" + label="End Alpha:" + label_width="70" + can_edit_text="true" + left="0" + width="275" + min_val="0" + max_val="1" + height="16" + top_pad="2"> + <slider.commit_callback + function="Particle.Edit" /> + </slider> + <slider + follows="left|top" + decimal_digits="3" + initial_value="0" + increment="0.001" + name="start_glow" + label="Start Glow:" + label_width="70" + can_edit_text="true" + left="0" + width="275" + min_val="0" + max_val="1" + height="16" + top_pad="2"> + <slider.commit_callback + function="Particle.Edit" /> + </slider> + <slider + follows="left|top" + decimal_digits="3" + initial_value="0" + increment="0.001" + name="end_glow" + label="End Glow:" + label_width="70" + can_edit_text="true" + left="0" + width="275" + min_val="0" + max_val="1" + height="16" + top_pad="2"> + <slider.commit_callback + function="Particle.Edit" /> + </slider> + </panel> + </accordion_tab> + <accordion_tab + expanded="false" + layout="topleft" + height="100" + name="omega_tab" + title="Acelleration/Omega" + fit_panel="true"> + <panel + follows="all" + height="100" + left="0" + name="omega_panel" + top="0" + width="313"> + <text + name="Acceleration_Label" + follows="left|top" + height="16" + left="0" + top="0" + width="76"> + Acceleration: + </text> + <slider + follows="left|top" + decimal_digits="2" + initial_value="0" + increment="0.01" + name="acceleration_x" + label="X:" + label_width="10" + can_edit_text="true" + left="0" + width="90" + min_val="-100" + max_val="100" + height="16" + top_pad="2"> + <slider.commit_callback + function="Particle.Edit" /> + </slider> + <slider + follows="left|top" + decimal_digits="2" + initial_value="0" + increment="0.01" + name="acceleration_y" + can_edit_text="true" + label="Y:" + label_width="10" + left_pad="2" + width="90" + min_val="-100" + max_val="100" + height="16" + top_delta="0"> + <slider.commit_callback + function="Particle.Edit" /> + </slider> + <slider + follows="left|top" + decimal_digits="2" + initial_value="0" + increment="0.01" + name="acceleration_z" + can_edit_text="true" + label="Z:" + label_width="10" + left_pad="2" + width="90" + min_val="-100" + max_val="100" + height="16" + top_delta="0"> + <slider.commit_callback + function="Particle.Edit" /> + </slider> + <text + name="Omega_Label" + follows="left|top" + height="16" + left="0" + top_pad="4" + width="76"> + Omega: + </text> + <slider + follows="left|top" + decimal_digits="2" + initial_value="0" + increment="0.01" + name="omega_x" + label="X:" + label_width="10" + can_edit_text="true" + left="0" + width="90" + min_val="-100" + max_val="100" + height="16" + top_pad="2"> + <slider.commit_callback + function="Particle.Edit" /> + </slider> + <slider + follows="left|top" + decimal_digits="2" + initial_value="0" + increment="0.01" + name="omega_y" + label="Y:" + label_width="10" + can_edit_text="true" + left_pad="2" + width="90" + min_val="-100" + max_val="100" + height="16" + top_delta="0"> + <slider.commit_callback + function="Particle.Edit" /> + </slider> + <slider + follows="left|top" + decimal_digits="2" + initial_value="0" + increment="0.01" + name="omega_z" + label="Z:" + label_width="10" + can_edit_text="true" + left_pad="2" + width="90" + min_val="-100" + max_val="100" + height="16" + top_delta="0"> + <slider.commit_callback + function="Particle.Edit" /> + </slider> + </panel> + </accordion_tab> + <accordion_tab + expanded="true" + layout="topleft" + height="100" + name="color_tab" + title="Color/Texture" + fit_panel="true"> + <panel + follows="all" + height="100" + left="0" + name="color_panel" + top="0" + width="313"> + <texture_picker + follows="top|right" + height="70" + width="60" + left="50" + can_apply_immediately="true" + name="texture_picker" + label="Texture" + top="4"> + <texture_picker.commit_callback + function="Particle.Edit" /> + </texture_picker> + <color_swatch + follows="left|top" + height="70" + width="60" + label="Start Color" + left_delta="17" + can_apply_immediately="true" + name="start_color_selector" + top_delta="0"> + <color_swatch.commit_callback + function="Particle.Edit" /> + </color_swatch> + <color_swatch + follows="left|top" + height="70" + width="60" + label="End Color" + left_pad="3" + name="end_color_selector" + can_apply_immediately="true" + top_delta="0"> + <color_swatch.commit_callback + function="Particle.Edit" /> + </color_swatch> + <combo_box + name="blend_func_src_combo" + follows="left|top" + height="22" + left_pad="4" + top_delta="0" + width="74"> + <combo_box.commit_callback + function="Particle.Edit" /> + <combo_box.item + label="Blend One" + name="blend_one" + value="blend_one" /> + <combo_box.item + label="Blend Zero" + name="blend_zero" + value="blend_zero" /> + <combo_box.item + label="Blend Dest Color" + name="blend_dest_color" + value="blend_dest_color" /> + <combo_box.item + label="Blend Src Color" + name="blend_src_color" + value="blend_src_color" /> + <combo_box.item + label="Blend 1 - Dest Color" + name="blend_one_minus_dest_color" + value="blend_one_minus_dest_color" /> + <combo_box.item + label="Blend 1 - Src Color" + name="blend_one_minus_src_color" + value="blend_one_minus_src_color" /> + <combo_box.item + label="Blend Src Alpha" + name="blend_src_alpha" + value="blend_src_alpha" /> + <combo_box.item + label="Blend 1 - Src Alpha" + name="blend_one_minus_src_alpha" + value="blend_one_minus_src_alpha" /> + </combo_box> + <combo_box + name="blend_func_dest_combo" + follows="left|top" + height="22" + left_delta="0" + top_pad="4" + width="74"> + <combo_box.commit_callback + function="Particle.Edit" /> + <combo_box.item + label="Blend One" + name="blend_one" + value="blend_one" /> + <combo_box.item + label="Blend Zero" + name="blend_zero" + value="blend_zero" /> + <combo_box.item + label="Blend Dest Color" + name="blend_dest_color" + value="blend_dest_color" /> + <combo_box.item + label="Blend Src Color" + name="blend_src_color" + value="blend_src_color" /> + <combo_box.item + label="Blend 1 - Dest Color" + name="blend_one_minus_dest_color" + value="blend_one_minus_dest_color" /> + <combo_box.item + label="Blend 1 - Src Color" + name="blend_one_minus_src_color" + value="blend_one_minus_src_color" /> + <combo_box.item + label="Blend Src Alpha" + name="blend_src_alpha" + value="blend_src_alpha" /> + <combo_box.item + label="Blend 1 - Src Alpha" + name="blend_one_minus_src_alpha" + value="blend_one_minus_src_alpha" /> + </combo_box> + </panel> + </accordion_tab> + </accordion> + </layout_panel> + <layout_panel + layout="topleft" + follows="all" + height="258" + left_pad="0" + name="checkbox_panel" + width="120"> + <text + name="Pattern_Label" + follows="left|top" + height="16" + left="0" + top="0" + width="100"> + Pattern: + </text> + <combo_box + follows="left|right|top" + name="pattern_type_combo" + top_pad="2" + left_delta="0" + right="-2"> + <combo_box.commit_callback + function="Particle.Edit" /> + <combo_box.item + label="Explode" + name="explode" + value="explode" /> + <combo_box.item + label="Angle" + name="angle" + value="angle" /> + <combo_box.item + label="Angle Cone" + name="angle_cone" + value="angle_cone" /> + <combo_box.item + label="Angle Cone Empty" + name="angle_cone_empty" + value="angle_cone_empty" /> + <combo_box.item + label="Drop" + name="drop" + value="drop" /> + </combo_box> + <check_box + follows="left|top" + height="16" + name="bounce_checkbox" + label="Bounce" + left="0" + top_pad="4" + width="80"> + <check_box.commit_callback + function="Particle.Edit" /> + </check_box> + <check_box + follows="left|top" + height="16" + name="emissive_checkbox" + label="Emissive" + top_pad="4" + left="0" + width="80"> + <check_box.commit_callback + function="Particle.Edit" /> + </check_box> + <check_box + follows="left|top" + height="16" + name="wind_checkbox" + label="Wind" + top_pad="4" + left="0" + width="80"> + <check_box.commit_callback + function="Particle.Edit" /> + </check_box> + <check_box + follows="left|top" + height="16" + name="ribbon_checkbox" + label="Ribbon" + top_pad="4" + left="0" + width="80"> + <check_box.commit_callback + function="Particle.Edit" /> + </check_box> + <check_box + follows="left|top" + height="16" + name="follow_source_checkbox" + label="Follow Source" + top_pad="4" + left="0" + width="120"> + <check_box.commit_callback + function="Particle.Edit" /> + </check_box> + <check_box + follows="left|top" + height="16" + name="follow_velocity_checkbox" + label="Follow Velocity" + top_pad="4" + width="120"> + <check_box.commit_callback + function="Particle.Edit" /> + </check_box> + <check_box + follows="left|top" + height="16" + name="interpolate_color_checkbox" + label="Interpolate Color" + top_pad="4" + width="120"> + <check_box.commit_callback + function="Particle.Edit" /> + </check_box> + <check_box + follows="left|top" + height="16" + name="interpolate_scale_checkbox" + label="Interpolate Scale" + top_pad="4" + width="120"> + <check_box.commit_callback + function="Particle.Edit" /> + </check_box> + <check_box + follows="left|top" + height="16" + name="target_position_checkbox" + label="Target Position" + top_pad="4" + width="120"> + <check_box.commit_callback + function="Particle.Edit" /> + </check_box> + <check_box + follows="left|top" + height="16" + name="target_linear_checkbox" + label="Target Linear" + top_pad="4" + width="120"> + <check_box.commit_callback + function="Particle.Edit" /> + </check_box> + <line_editor + follows="left|right|top" + left="0" + name="target_key_input" + height="20" + right="-48"> + <line_editor.commit_callback + function="Particle.Edit" /> + </line_editor> + <button + follows="right|top" + image_overlay="StopReload_Over" + left_pad="4" + name="clear_target_button" + tool_tip="Clear target object or avatar" + width="20" + height="20" /> + <button + follows="right|top" + image_overlay="Inv_Object" + left_pad="2" + name="pick_target_button" + tool_tip="Click here to select the particle target object or avatar." + width="20" + height="20" /> + <button + follows="left|top" + height="24" + name="copy_button" + label="Copy" + tool_tip="Copies the particle system's parameter as LSL script to the clipboard." + top_pad="7" + left="0" + width="80" /> + <button + follows="left|top" + height="24" + name="inject_button" + label="Inject" + tool_tip="Inject this particle system into the selected object." + left_pad="4" + width="80" /> + </layout_panel> + </layout_stack> +</floater> diff --git a/indra/newview/skins/default/xui/en/menu_object.xml b/indra/newview/skins/default/xui/en/menu_object.xml index 6eff009203be3174f4d00c04b2401423a156d9e7..858db3eb14838f4d1ad6d497490b45ab90fed376 100755 --- a/indra/newview/skins/default/xui/en/menu_object.xml +++ b/indra/newview/skins/default/xui/en/menu_object.xml @@ -29,6 +29,14 @@ <menu_item_call.on_enable function="EnableEdit"/> </menu_item_call> + <menu_item_call + label="Edit Particles" + name="Menu Object Edit Particles"> + <menu_item_call.on_click + function="Object.EditParticles" /> + <menu_item_call.on_enable + function="Object.EnableEditParticles" /> + </menu_item_call> <menu_item_call enabled="false" label="Open" diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 2b68b6524d329866c720ac7fa6b027f75b9b45d6..63a05a607a144bc41e9862f5b53a2317089cf5d3 100755 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -1049,6 +1049,14 @@ <menu_item_call.on_enable function="Tools.EnableTakeCopy" /> </menu_item_call> + <menu_item_call + label="Edit Particles" + name="Menu Object Edit Particles"> + <menu_item_call.on_click + function="Object.EditParticles" /> + <menu_item_call.on_enable + function="Object.EnableEditParticles" /> + </menu_item_call> <menu_item_call label="Save Back to Object Contents" name="Save Object Back to Object Contents"> diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index b1e645a62e548aa23eaabecefe10866d6b838024..84e6c6e1ecd277e9ed8062c2371af8f0d9b67e9c 100755 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -10749,6 +10749,68 @@ the region "[REGION]"? Auto-accepted [ITEM] from [NAME] and placed in inventory. </notification> + <notification + icon="alertmodal.tga" + name="ParticleScriptFindFolderFailed" + type="alertmodal"> +Could not find a folder for the new script in inventory. + </notification> + + <notification + icon="alertmodal.tga" + name="ParticleScriptCreationFailed" + type="alertmodal"> +Could not create new script for this particle system. + </notification> + + <notification + icon="alertmodal.tga" + name="ParticleScriptNotFound" + type="alertmodal"> +Could not find the newly created script for this particle system. + </notification> + + <notification + icon="alertmodal.tga" + name="ParticleScriptCreateTempFileFailed" + type="alertmodal"> +Could not create temporary file for script upload. + <form name="form"> + <ignore name="ignore" + text="A particle script was copied to my clipboard"/> + </form> + </notification> + + <notification + icon="notify.tga" + name="ParticleScriptInjected" + type="alertmodal"> +Particle script was injected successfully. + <form name="form"> + <ignore name="ignore" + text="A particle script was injected to an object."/> + </form> + </notification> + + <notification + icon="alertmodal.tga" + name="ParticleScriptCapsFailed" + type="alertmodal"> +Failed to inject script into object. Request for capabilities returned an empty address. + </notification> + + <notification + icon="notify.tga" + name="ParticleScriptCopiedToClipboard" + type="alertmodal"> +The LSL script to create this particle system has been copied to your clipboard. You can now paste it into a new script to use it. + <form name="form"> + <ignore name="ignore" + text="A particle script was copied to my clipboard"/> + </form> + </notification> + + <notification icon="alertmodal.tga" name="RevokedMapRights"