Forked from
Alchemy Viewer / Alchemy Viewer
1925 commits behind, 124 commits ahead of the upstream repository.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
alfloaterparticleeditor.cpp 26.01 KiB
/**
* @file alfloaterparticleeditor.cpp
* @brief Particle Editor
*
* Copyright (C) 2011, Zi Ree @ Second Life
* Copyright (C) 2015, Cinder Roxley <cinder@sdf.org>
* Copyright (C) 2020, Rye Mutt <rye@alchemyviewer.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 "alfloaterparticleeditor.h"
#include "llagent.h"
#include "llappviewer.h"
#include "llcheckboxctrl.h"
#include "llclipboard.h"
#include "llcolorswatch.h"
#include "llcombobox.h"
#include "llfoldertype.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 "llviewerassetupload.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") }
};
static const std::string PARTICLE_SCRIPT_NAME = "New Particle Script";
ALFloaterParticleEditor::ALFloaterParticleEditor(const LLSD& key)
: LLFloater(key), mChanged(false), mCloseAfterSave(false), mObject(nullptr), mTexture(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), mAcellerationXCtrl(nullptr)
, mAcellerationYCtrl(nullptr), mAcellerationZCtrl(nullptr), mOmegaXCtrl(nullptr)
, mOmegaYCtrl(nullptr), mOmegaZCtrl(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), mTargetKeyInput(nullptr)
, mClearTargetButton(nullptr), mPickTargetButton(nullptr), mCopyToLSLButton(nullptr)
, mInjectScriptButton(nullptr), mStartColorSelector(nullptr), mEndColorSelector(nullptr)
{
mCommitCallbackRegistrar.add("Particle.Edit", [this](LLUICtrl* ctrl, const LLSD& param) { onParameterChange(); });
mDefaultParticleTexture = LLViewerFetchedTexture::sPixieSmallImagep;
}
ALFloaterParticleEditor::~ALFloaterParticleEditor()
{
clearParticles();
}
BOOL ALFloaterParticleEditor::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([this](LLUICtrl* ctrl, const LLSD& param) { onClickClearTarget(); });
mPickTargetButton->setCommitCallback([this](LLUICtrl* ctrl, const LLSD& param) { onClickTargetPicker(); });
mInjectScriptButton->setCommitCallback([this](LLUICtrl* ctrl, const LLSD& param) { injectScript(); });
mCopyToLSLButton->setCommitCallback([this](LLUICtrl* ctrl, const LLSD& param) { onClickCopy(); });
mBlendFuncSrcCombo->setValue("blend_src_alpha");
mBlendFuncDestCombo->setValue("blend_one_minus_src_alpha");
onParameterChange();
return LLFloater::postBuild();
}
BOOL ALFloaterParticleEditor::canClose()
{
if (!hasChanged())
{
return TRUE;
}
else
{
// Bring up view-modal dialog: Save changes? Yes, No, Cancel
LLNotificationsUtil::add("ParticleSaveChanges", LLSD(), LLSD(), boost::bind(&ALFloaterParticleEditor::handleSaveDialog, this, _1, _2));
return FALSE;
}
}
bool ALFloaterParticleEditor::handleSaveDialog(const LLSD& notification, const LLSD& response)
{
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
switch (option)
{
case 0: // "Yes"
// close after saving
mCloseAfterSave = true;
injectScript();
break;
case 1: // "No"
mChanged = false;
closeFloater();
break;
case 2: // "Cancel"
default:
break;
}
return false;
}
void ALFloaterParticleEditor::clearParticles()
{
if (!mObject)
return;
LL_DEBUGS("ParticleEditor") << "clearing particles from " << mObject->getID() << LL_ENDL;
LLViewerPartSim::getInstance()->clearParticlesByOwnerID(mObject->getID());
}
void ALFloaterParticleEditor::updateParticles()
{
if (!mObject)
return;
clearParticles();
LLPointer<LLViewerPartSourceScript> pss = LLViewerPartSourceScript::createPSS(mObject, mParticles);
pss->setOwnerUUID(mObject->getID());
pss->setImage(mTexture);
LLViewerPartSim::getInstance()->addPartSource(pss);
}
void ALFloaterParticleEditor::setObject(LLViewerObject* objectp)
{
if (objectp)
{
mObject = objectp;
LL_DEBUGS("ParticleEditor") << "adding particles to " << mObject->getID() << LL_ENDL;
updateParticles();
}
}
void ALFloaterParticleEditor::onParameterChange()
{
mChanged = true;
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.setUseNewAngle();
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 ALFloaterParticleEditor::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 ALFloaterParticleEditor::onClickClearTarget()
{
mTargetKeyInput->clear();
onParameterChange();
}
void ALFloaterParticleEditor::onClickTargetPicker()
{
mPickTargetButton->setToggleState(TRUE);
mPickTargetButton->setEnabled(FALSE);
LLToolObjPicker::getInstance()->setExitCallback(onTargetPicked, this);
LLToolMgr::getInstance()->setTransientTool(LLToolObjPicker::getInstance());
}
// static
void ALFloaterParticleEditor::onTargetPicked(void* userdata)
{
ALFloaterParticleEditor* self = static_cast<ALFloaterParticleEditor*>(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();
}
}
/* static */
std::string ALFloaterParticleEditor::lslVector(const F32 x, const F32 y, const F32 z)
{
return llformat("<%f,%f,%f>", x, y, z);
}
/* static */
std::string ALFloaterParticleEditor::lslColor(const LLColor4& color)
{
return lslVector(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE]);
}
std::string ALFloaterParticleEditor::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 target_key = mTargetKeyInput->getValue().asUUID();
std::string key_string = "llGetKey()";
if (target_key.notNull() && target_key != mObject->getID())
{
key_string = "(key) \"" + target_key.asString() + "\"";
}
LLUUID texture_key = mTexture->getID();
std::string texture_string;
if (texture_key.notNull() && texture_key != IMG_DEFAULT && texture_key != mDefaultParticleTexture->getID())
{
texture_string = texture_key.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]", key_string);
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]", texture_string);
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);
const std::string delimiter = " |\n ";
std::string flags_string = "";
if (mBounceCheckBox->getValue())
flags_string += delimiter + "PSYS_PART_BOUNCE_MASK";
if (mEmissiveCheckBox->getValue())
flags_string += delimiter + "PSYS_PART_EMISSIVE_MASK";
if (mFollowSourceCheckBox->getValue())
flags_string += delimiter + "PSYS_PART_FOLLOW_SRC_MASK";
if (mFollowVelocityCheckBox->getValue())
flags_string += delimiter + "PSYS_PART_FOLLOW_VELOCITY_MASK";
if (mInterpolateColorCheckBox->getValue())
flags_string += delimiter + "PSYS_PART_INTERP_COLOR_MASK";
if (mInterpolateScaleCheckBox->getValue())
flags_string += delimiter + "PSYS_PART_INTERP_SCALE_MASK";
if (mTargetLinearCheckBox->getValue())
flags_string += delimiter + "PSYS_PART_TARGET_LINEAR_MASK";
if (mTargetPositionCheckBox->getValue())
flags_string += delimiter + "PSYS_PART_TARGET_POS_MASK";
if (mWindCheckBox->getValue())
flags_string += delimiter + "PSYS_PART_WIND_MASK";
if (mRibbonCheckBox->getValue())
flags_string += delimiter + "PSYS_PART_RIBBON_MASK";
LLStringUtil::replaceString(script, "[FLAGS]", flags_string);
LL_DEBUGS("ParticleEditor") << "\n" << script << LL_ENDL;
return script;
}
void ALFloaterParticleEditor::onClickCopy()
{
mChanged = false;
const std::string script = createScript();
if (!script.empty())
{
getWindow()->copyTextToClipboard(utf8str_to_wstring(script));
LLNotificationsUtil::add("ParticleScriptCopiedToClipboard");
}
}
void ALFloaterParticleEditor::injectScript()
{
mChanged = false;
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,
NO_INV_SUBTYPE,
perm.getMaskNextOwner(),
callback);
setCanClose(FALSE);
}
void ALFloaterParticleEditor::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.getRegionCapability("UpdateScriptAgent");
if (url.empty())
{
LLNotificationsUtil::add("ParticleScriptFailed");
return;
}
const std::string script = createScript();
LLBufferedAssetUploadInfo::taskUploadFinish_f proc =
boost::bind(&ALFloaterParticleEditor::finishUpload, _1, _2, _3, _4, true, mObject->getID());
LLResourceUploadInfo::ptr_t uploadInfo(new LLScriptAssetUpload(mObject->getID(), inventoryItemID,
LLScriptAssetUpload::MONO, true, LLUUID::null, script, proc, nullptr));
LLViewerAssetUpload::EnqueueInventoryUpload(url, uploadInfo);
if (mCloseAfterSave) closeFloater();
}
// ---------------------------------- Callbacks ----------------------------------
ALFloaterParticleEditor::LLParticleScriptCreationCallback::
LLParticleScriptCreationCallback(ALFloaterParticleEditor* editor)
: mEditor(editor)
{
}
void ALFloaterParticleEditor::LLParticleScriptCreationCallback::fire(const LLUUID& inventoryItem)
{
if (!gDisconnected && !LLAppViewer::instance()->quitRequested() && mEditor)
{
mEditor->callbackReturned(inventoryItem);
}
}
/* static */
void ALFloaterParticleEditor::finishUpload(LLUUID itemId, LLUUID taskId, LLUUID newAssetId,
LLSD response, bool isRunning, LLUUID objectId)
{
// make sure there's still an object rezzed
LLViewerObject* object = gObjectList.findObject(objectId);
if (!object || object->isDead())
{
LL_WARNS("ParticleEditor") << "Failed to inject script in object: " << objectId.asString() << LL_ENDL;
return;
}
auto* script = gInventory.getItem(itemId);
object->saveScript(script, TRUE, FALSE);
LLNotificationsUtil::add("ParticleScriptInjected");
}