Esempio n. 1
0
//-----------------------------------------------------------------------------
// setInfo()
//-----------------------------------------------------------------------------
BOOL LLPolyMorphTarget::setInfo(LLPolyMorphTargetInfo* info)
{
	llassert(mInfo == NULL);
	if (info->mID < 0)
		return FALSE;
	mInfo = info;
	mID = info->mID;
	setWeight(getDefaultWeight(), FALSE );

	LLVOAvatar* avatarp = mMesh->getAvatar();
	LLPolyMorphTargetInfo::volume_info_list_t::iterator iter;
	for (iter = getInfo()->mVolumeInfoList.begin(); iter != getInfo()->mVolumeInfoList.end(); iter++)
	{
		LLPolyVolumeMorphInfo *volume_info = &(*iter);
		for (S32 i = 0; i < avatarp->mNumCollisionVolumes; i++)
		{
			if (avatarp->mCollisionVolumes[i].getName() == volume_info->mName)
			{
				mVolumeMorphs.push_back(LLPolyVolumeMorph(&avatarp->mCollisionVolumes[i],
														  volume_info->mScale,
														  volume_info->mPos));
				break;
			}
		}
	}

	mMorphData = mMesh->getMorphData(getInfo()->mMorphName);
	if (!mMorphData)
	{
		llwarns << "No morph target named " << getInfo()->mMorphName << " found in mesh." << llendl;
		return FALSE;  // Continue, ignoring this tag
	}
	return TRUE;
}
Esempio n. 2
0
//-----------------------------------------------------------------------------
// apply()
//-----------------------------------------------------------------------------
void LLPolySkeletalDistortion::apply( ESex avatar_sex )
{
	F32 effective_weight = ( getSex() & avatar_sex ) ? mCurWeight : getDefaultWeight();

	LLJoint* joint;
	joint_vec_map_t::iterator iter;

	for (iter = mJointScales.begin();
		 iter != mJointScales.end();
		 iter++)
	{
		joint = iter->first;
		LLVector3 newScale = joint->getScale();
		LLVector3 scaleDelta = iter->second;
		newScale = newScale + (effective_weight * scaleDelta) - (mLastWeight * scaleDelta);
		joint->setScale(newScale);
	}

	for (iter = mJointOffsets.begin();
		 iter != mJointOffsets.end();
		 iter++)
	{
		joint = iter->first;
		LLVector3 newPosition = joint->getPosition();
		LLVector3 positionDelta = iter->second;
		newPosition = newPosition + (effective_weight * positionDelta) - (mLastWeight * positionDelta);
		joint->setPosition(newPosition);
	}

	if (mLastWeight != mCurWeight && !mIsAnimating)
	{
		mAvatar->setSkeletonSerialNum(mAvatar->getSkeletonSerialNum() + 1);
	}
	mLastWeight = mCurWeight;
}
Esempio n. 3
0
BOOL LLDriverParam::setInfo(LLDriverParamInfo *info)
{
	llassert(mInfo == NULL);
	if (info->mID < 0)
		return FALSE;
	mInfo = info;
	mID = info->mID;

	setWeight(getDefaultWeight(), FALSE );
	
	LLDriverParamInfo::entry_info_list_t::iterator iter;
	mDriven.reserve(getInfo()->mDrivenInfoList.size());
	for (iter = getInfo()->mDrivenInfoList.begin(); iter != getInfo()->mDrivenInfoList.end(); iter++)
	{
		LLDrivenEntryInfo *driven_info = &(*iter);
		S32 driven_id = driven_info->mDrivenID;
		LLViewerVisualParam* param = (LLViewerVisualParam*)mAvatarp->getVisualParam( driven_id );
		if (param)
		{
			mDriven.push_back(LLDrivenEntry( param, driven_info ));
		}
		else
		{
			llerrs << "<driven> Unable to resolve driven parameter: " << driven_id << llendl;
			mInfo = NULL;
			return FALSE;
		}
	}
	
	return TRUE;
}
Esempio n. 4
0
LLColor4 LLTexLayerParamColor::getNetColor() const
{
	const LLTexLayerParamColorInfo *info = (LLTexLayerParamColorInfo *)getInfo();
	
	llassert(info->mNumColors >= 1);

	F32 effective_weight = (mAvatar && (mAvatar->getSex() & getSex())) ? mCurWeight : getDefaultWeight();

	S32 index_last = info->mNumColors - 1;
	F32 scaled_weight = effective_weight * index_last;
	S32 index_start = (S32) scaled_weight;
	S32 index_end = index_start + 1;
	if (index_start == index_last)
	{
		return info->mColors[index_last];
	}
	else
	{
		F32 weight = scaled_weight - index_start;
		const LLColor4 *start = &info->mColors[ index_start ];
		const LLColor4 *end   = &info->mColors[ index_end ];
		return LLColor4((1.f - weight) * start->mV[VX] + weight * end->mV[VX],
						(1.f - weight) * start->mV[VY] + weight * end->mV[VY],
						(1.f - weight) * start->mV[VZ] + weight * end->mV[VZ],
						(1.f - weight) * start->mV[VW] + weight * end->mV[VW]);
	}
}
Esempio n. 5
0
BOOL LLTexLayerParamAlpha::getSkip() const
{
	if (!mTexLayer)
	{
		return TRUE;
	}

	const LLVOAvatar *avatar = mTexLayer->getTexLayerSet()->getAvatar();

	if (((LLTexLayerParamAlphaInfo *)getInfo())->mSkipIfZeroWeight)
	{
		F32 effective_weight = (avatar->getSex() & getSex()) ? mCurWeight : getDefaultWeight();
		if (is_approx_zero(effective_weight)) 
		{
			return TRUE;
		}
	}

	LLWearableType::EType type = (LLWearableType::EType)getWearableType();
	if ((type != LLWearableType::WT_INVALID) && !avatar->isWearingWearableType(type))
	{
		return TRUE;
	}

	return FALSE;
}
BOOL LLViewerVisualParam::setInfo(LLViewerVisualParamInfo *info)
{
	llassert(mInfo == NULL);
	if (info->mID < 0)
		return FALSE;
	mInfo = info;
	mID = info->mID;
	setWeight(getDefaultWeight(), FALSE );
	return TRUE;
}
BOOL LLViewerVisualParam::setInfo(LLViewerVisualParamInfo *info)
{
	llassert(mInfo == NULL);
	if (info->mID < 0)
		return FALSE;
	mInfo = info;
	mID = info->mID;
	// <FS:Ansariel> [Legacy Bake]
	//setWeight(getDefaultWeight());
	setWeight(getDefaultWeight(), FALSE);
	return TRUE;
}
BOOL LLPolySkeletalDistortion::setInfo(LLPolySkeletalDistortionInfo *info)
{
        llassert(mInfo == NULL);
        if (info->mID < 0)
                return FALSE;
        mInfo = info;
        mID = info->mID;
        setWeight(getDefaultWeight(), FALSE );

        LLPolySkeletalDistortionInfo::bone_info_list_t::iterator iter;
        for (iter = getInfo()->mBoneInfoList.begin(); iter != getInfo()->mBoneInfoList.end(); iter++)
        {
                LLPolySkeletalBoneInfo *bone_info = &(*iter);
                LLJoint* joint = mAvatar->getJoint(bone_info->mBoneName);
                if (!joint)
                {
                        LL_WARNS() << "Joint " << bone_info->mBoneName << " not found." << LL_ENDL;
                        continue;
                }

                if (mJointScales.find(joint) != mJointScales.end())
                {
                        LL_WARNS() << "Scale deformation already supplied for joint " << joint->getName() << "." << LL_ENDL;
                }

                // store it
                mJointScales[joint] = bone_info->mScaleDeformation;

                // apply to children that need to inherit it
                for (LLJoint::child_list_t::iterator iter = joint->mChildren.begin();
                     iter != joint->mChildren.end(); ++iter)
                {
                        LLAvatarJoint* child_joint = (LLAvatarJoint*)(*iter);
                        if (child_joint->inheritScale())
                        {
                                LLVector3 childDeformation = LLVector3(child_joint->getScale());
                                childDeformation.scaleVec(bone_info->mScaleDeformation);
                                mJointScales[child_joint] = childDeformation;
                        }
                }

                if (bone_info->mHasPositionDeformation)
                {
                        if (mJointOffsets.find(joint) != mJointOffsets.end())
                        {
                                LL_WARNS() << "Offset deformation already supplied for joint " << joint->getName() << "." << LL_ENDL;
                        }
                        mJointOffsets[joint] = bone_info->mPositionDeformation;
                }
        }
        return TRUE;
}
BOOL LLDriverParam::setInfo(LLDriverParamInfo *info)
{
	llassert(mInfo == NULL);
	if (info->mID < 0)
		return FALSE;
	mInfo = info;
	mID = info->mID;
	info->mDriverParam = this;

	setWeight(getDefaultWeight());

	return TRUE;
}
//-----------------------------------------------------------------------------
// setInfo()
//-----------------------------------------------------------------------------
BOOL LLPolyMorphTarget::setInfo(LLPolyMorphTargetInfo* info)
{
	llassert(mInfo == NULL);
	if (info->mID < 0)
		return FALSE;
	mInfo = info;
	mID = info->mID;
	// <FS:Ansariel> [Legacy Bake]
	//setWeight(getDefaultWeight());
	setWeight(getDefaultWeight(), FALSE);

	LLAvatarAppearance* avatarp = mMesh->getAvatar();
	LLPolyMorphTargetInfo::volume_info_list_t::iterator iter;
	for (iter = getInfo()->mVolumeInfoList.begin(); iter != getInfo()->mVolumeInfoList.end(); iter++)
	{
		LLPolyVolumeMorphInfo *volume_info = &(*iter);
		for (S32 i = 0; i < avatarp->mNumCollisionVolumes; i++)
		{
			if (avatarp->mCollisionVolumes[i].getName() == volume_info->mName)
			{
				mVolumeMorphs.push_back(LLPolyVolumeMorph(&avatarp->mCollisionVolumes[i],
														  volume_info->mScale,
														  volume_info->mPos));
				break;
			}
		}
	}

	std::string morph_param_name = getInfo()->mMorphName;
	
	mMorphData = mMesh->getMorphData(morph_param_name);
	if (!mMorphData)
	{
		const std::string driven_tag = "_Driven";
		U32 pos = morph_param_name.find(driven_tag);
		if (pos > 0)
		{
			morph_param_name = morph_param_name.substr(0,pos);
			mMorphData = mMesh->getMorphData(morph_param_name);
		}
	}
	if (!mMorphData)
	{
		LL_WARNS() << "No morph target named " << morph_param_name << " found in mesh." << LL_ENDL;
		return FALSE;  // Continue, ignoring this tag
	}
	return TRUE;
}
void LLPolySkeletalDistortion::apply( ESex avatar_sex )
{
	LL_RECORD_BLOCK_TIME(FTM_POLYSKELETAL_DISTORTION_APPLY);

        F32 effective_weight = ( getSex() & avatar_sex ) ? mCurWeight : getDefaultWeight();

        LLJoint* joint;
        joint_vec_map_t::iterator iter;

	for (iter = mJointScales.begin();
		 iter != mJointScales.end();
		 iter++)
	{
		joint = iter->first;
		LLVector3 newScale = joint->getScale();
		LLVector3 scaleDelta = iter->second;
		newScale = newScale + (effective_weight * scaleDelta) - (mLastWeight * scaleDelta);
				//An aspect of attached mesh objects (which contain joint offsets) that need to be cleaned up when detached
				// needed? // joint->storeScaleForReset( newScale );				
		joint->setScale(newScale);
	}

	for (iter = mJointOffsets.begin();
		 iter != mJointOffsets.end();
		 iter++)
	{
		joint = iter->first;
		LLVector3 newPosition = joint->getPosition();
		LLVector3 positionDelta = iter->second;
		newPosition = newPosition + (effective_weight * positionDelta) - (mLastWeight * positionDelta);
		joint->setPosition(newPosition);
	}

	if (mLastWeight != mCurWeight && !mIsAnimating)
	{
		mAvatar->setSkeletonSerialNum(mAvatar->getSkeletonSerialNum() + 1);
	}
	mLastWeight = mCurWeight;
}
void LLPolyMorphTarget::apply( ESex avatar_sex )
{
	if (!mMorphData || mNumMorphMasksPending > 0)
	{
		return;
	}

	LL_RECORD_BLOCK_TIME(FTM_APPLY_MORPH_TARGET);

	mLastSex = avatar_sex;

	// Check for NaN condition (NaN is detected if a variable doesn't equal itself.
	if (mCurWeight != mCurWeight)
	{
		mCurWeight = 0.0;
	}
	if (mLastWeight != mLastWeight)
	{
		mLastWeight = mCurWeight+.001;
	}

	// perform differential update of morph
	F32 delta_weight = ( getSex() & avatar_sex ) ? (mCurWeight - mLastWeight) : (getDefaultWeight() - mLastWeight);
	// store last weight
	mLastWeight += delta_weight;

	if (delta_weight != 0.f)
	{
		llassert(!mMesh->isLOD());
		LLVector4a *coords = mMesh->getWritableCoords();

		LLVector4a *scaled_normals = mMesh->getScaledNormals();
		LLVector4a *normals = mMesh->getWritableNormals();

		LLVector4a *scaled_binormals = mMesh->getScaledBinormals();
		LLVector4a *binormals = mMesh->getWritableBinormals();

		LLVector4a *clothing_weights = mMesh->getWritableClothingWeights();
		LLVector2 *tex_coords = mMesh->getWritableTexCoords();

		F32 *maskWeightArray = (mVertMask) ? mVertMask->getMorphMaskWeights() : NULL;

		for(U32 vert_index_morph = 0; vert_index_morph < mMorphData->mNumIndices; vert_index_morph++)
		{
			S32 vert_index_mesh = mMorphData->mVertexIndices[vert_index_morph];

			F32 maskWeight = 1.f;
			if (maskWeightArray)
			{
				maskWeight = maskWeightArray[vert_index_morph];
			}


			LLVector4a pos = mMorphData->mCoords[vert_index_morph];
			pos.mul(delta_weight*maskWeight);
			coords[vert_index_mesh].add(pos);

			if (getInfo()->mIsClothingMorph && clothing_weights)
			{
				LLVector4a clothing_offset = mMorphData->mCoords[vert_index_morph];
				clothing_offset.mul(delta_weight * maskWeight);
				LLVector4a* clothing_weight = &clothing_weights[vert_index_mesh];
				clothing_weight->add(clothing_offset);
				clothing_weight->getF32ptr()[VW] = maskWeight;
			}

			// calculate new normals based on half angles
			LLVector4a norm = mMorphData->mNormals[vert_index_morph];
			norm.mul(delta_weight*maskWeight*NORMAL_SOFTEN_FACTOR);
			scaled_normals[vert_index_mesh].add(norm);
			norm = scaled_normals[vert_index_mesh];

			// guard against degenerate input data before we create NaNs below!
			//
			norm.normalize3fast();
			normals[vert_index_mesh] = norm;

			// calculate new binormals
			LLVector4a binorm = mMorphData->mBinormals[vert_index_morph];

			// guard against degenerate input data before we create NaNs below!
			//
			if (!binorm.isFinite3() || (binorm.dot3(binorm).getF32() <= F_APPROXIMATELY_ZERO))
			{
				binorm.set(1,0,0,1);
			}

			binorm.mul(delta_weight*maskWeight*NORMAL_SOFTEN_FACTOR);
			scaled_binormals[vert_index_mesh].add(binorm);
			LLVector4a tangent;
			tangent.setCross3(scaled_binormals[vert_index_mesh], norm);
			LLVector4a& normalized_binormal = binormals[vert_index_mesh];

			normalized_binormal.setCross3(norm, tangent); 
			normalized_binormal.normalize3fast();
			
			tex_coords[vert_index_mesh] += mMorphData->mTexCoords[vert_index_morph] * delta_weight * maskWeight;
		}

		// now apply volume changes
		for( volume_list_t::iterator iter = mVolumeMorphs.begin(); iter != mVolumeMorphs.end(); iter++ )
		{
			LLPolyVolumeMorph* volume_morph = &(*iter);
			LLVector3 scale_delta = volume_morph->mScale * delta_weight;
			LLVector3 pos_delta = volume_morph->mPos * delta_weight;
			
			volume_morph->mVolume->setScale(volume_morph->mVolume->getScale() + scale_delta);
			volume_morph->mVolume->setPosition(volume_morph->mVolume->getPosition() + pos_delta);
		}
	}

	if (mNext)
	{
		mNext->apply(avatar_sex);
	}
}
Esempio n. 13
0
//-----------------------------------------------------------------------------
// apply()
//-----------------------------------------------------------------------------
void LLPolyMorphTarget::apply( ESex avatar_sex )
{
	if (!mMorphData || mNumMorphMasksPending > 0)
	{
		return;
	}

	mLastSex = avatar_sex;

	// Check for NaN condition (NaN is detected if a variable doesn't equal itself.
	if (mCurWeight != mCurWeight)
	{
		mCurWeight = 0.0;
	}
	if (mLastWeight != mLastWeight)
	{
		mLastWeight = mCurWeight+.001;
	}

	// perform differential update of morph
	F32 delta_weight = ( getSex() & avatar_sex ) ? (mCurWeight - mLastWeight) : (getDefaultWeight() - mLastWeight);
	// store last weight
	mLastWeight += delta_weight;

	if (delta_weight != 0.f)
	{
		llassert(!mMesh->isLOD());
		LLVector4 *coords = mMesh->getWritableCoords();

		LLVector3 *scaled_normals = mMesh->getScaledNormals();
		LLVector4 *normals = mMesh->getWritableNormals();

		LLVector3 *scaled_binormals = mMesh->getScaledBinormals();
		LLVector3 *binormals = mMesh->getWritableBinormals();

		LLVector4 *clothing_weights = mMesh->getWritableClothingWeights();
		LLVector2 *tex_coords = mMesh->getWritableTexCoords();

		F32 *maskWeightArray = (mVertMask) ? mVertMask->getMorphMaskWeights() : NULL;

		for(U32 vert_index_morph = 0; vert_index_morph < mMorphData->mNumIndices; vert_index_morph++)
		{
			S32 vert_index_mesh = mMorphData->mVertexIndices[vert_index_morph];

			F32 maskWeight = 1.f;
			if (maskWeightArray)
			{
				maskWeight = maskWeightArray[vert_index_morph];
			}

			coords[vert_index_mesh] += LLVector4(mMorphData->mCoords[vert_index_morph] * delta_weight * maskWeight);

			if (getInfo()->mIsClothingMorph && clothing_weights)
			{
				LLVector3 clothing_offset = mMorphData->mCoords[vert_index_morph] * delta_weight * maskWeight;
				LLVector4* clothing_weight = &clothing_weights[vert_index_mesh];
				clothing_weight->mV[VX] += clothing_offset.mV[VX];
				clothing_weight->mV[VY] += clothing_offset.mV[VY];
				clothing_weight->mV[VZ] += clothing_offset.mV[VZ];
				clothing_weight->mV[VW] = maskWeight;
			}

			// calculate new normals based on half angles
			scaled_normals[vert_index_mesh] += mMorphData->mNormals[vert_index_morph] * delta_weight * maskWeight * NORMAL_SOFTEN_FACTOR;
			LLVector3 normalized_normal = scaled_normals[vert_index_mesh];
			normalized_normal.normVec();
			normals[vert_index_mesh] = LLVector4(normalized_normal);

			// calculate new binormals
			scaled_binormals[vert_index_mesh] += mMorphData->mBinormals[vert_index_morph] * delta_weight * maskWeight * NORMAL_SOFTEN_FACTOR;
			LLVector3 tangent = scaled_binormals[vert_index_mesh] % normalized_normal;
			LLVector3 normalized_binormal = normalized_normal % tangent; 
			normalized_binormal.normVec();
			binormals[vert_index_mesh] = normalized_binormal;

			tex_coords[vert_index_mesh] += mMorphData->mTexCoords[vert_index_morph] * delta_weight * maskWeight;
		}

		// now apply volume changes
		for( volume_list_t::iterator iter = mVolumeMorphs.begin(); iter != mVolumeMorphs.end(); iter++ )
		{
			LLPolyVolumeMorph* volume_morph = &(*iter);
			LLVector3 scale_delta = volume_morph->mScale * delta_weight;
			LLVector3 pos_delta = volume_morph->mPos * delta_weight;
			
			volume_morph->mVolume->setScale(volume_morph->mVolume->getScale() + scale_delta);
			volume_morph->mVolume->setPosition(volume_morph->mVolume->getPosition() + pos_delta);
		}
	}

	if (mNext)
	{
		mNext->apply(avatar_sex);
	}
}
Esempio n. 14
0
BOOL LLTexLayerParamAlpha::render(S32 x, S32 y, S32 width, S32 height)
{
	BOOL success = TRUE;

	if (!mTexLayer)
	{
		return success;
	}

	F32 effective_weight = (mTexLayer->getTexLayerSet()->getAvatar()->getSex() & getSex()) ? mCurWeight : getDefaultWeight();
	BOOL weight_changed = effective_weight != mCachedEffectiveWeight;
	if (getSkip())
	{
		return success;
	}

	LLTexLayerParamAlphaInfo *info = (LLTexLayerParamAlphaInfo *)getInfo();
	gGL.flush();
	if (info->mMultiplyBlend)
	{
		gGL.blendFunc(LLRender::BF_DEST_ALPHA, LLRender::BF_ZERO); // Multiplication: approximates a min() function
	}
	else
	{
		gGL.setSceneBlendType(LLRender::BT_ADD);  // Addition: approximates a max() function
	}

	if (!info->mStaticImageFileName.empty() && !mStaticImageInvalid)
	{
		if (mStaticImageTGA.isNull())
		{
			// Don't load the image file until we actually need it the first time.  Like now.
			mStaticImageTGA = LLTexLayerStaticImageList::getInstance()->getImageTGA(info->mStaticImageFileName);  
			// We now have something in one of our caches
			LLTexLayerSet::sHasCaches |= mStaticImageTGA.notNull() ? TRUE : FALSE;

			if (mStaticImageTGA.isNull())
			{
				llwarns << "Unable to load static file: " << info->mStaticImageFileName << llendl;
				mStaticImageInvalid = TRUE; // don't try again.
				return FALSE;
			}
		}

		const S32 image_tga_width = mStaticImageTGA->getWidth();
		const S32 image_tga_height = mStaticImageTGA->getHeight(); 
		if (!mCachedProcessedTexture ||
			(mCachedProcessedTexture->getWidth() != image_tga_width) ||
			(mCachedProcessedTexture->getHeight() != image_tga_height) ||
			(weight_changed))
		{
//			llinfos << "Building Cached Alpha: " << mName << ": (" << mStaticImageRaw->getWidth() << ", " << mStaticImageRaw->getHeight() << ") " << effective_weight << llendl;
			mCachedEffectiveWeight = effective_weight;

			if (!mCachedProcessedTexture)
			{
				mCachedProcessedTexture = LLViewerTextureManager::getLocalTexture(image_tga_width, image_tga_height, 1, FALSE);

				// We now have something in one of our caches
				LLTexLayerSet::sHasCaches |= mCachedProcessedTexture ? TRUE : FALSE;

				mCachedProcessedTexture->setExplicitFormat(GL_ALPHA8, GL_ALPHA);
			}

			// Applies domain and effective weight to data as it is decoded. Also resizes the raw image if needed.
			mStaticImageRaw = NULL;
			mStaticImageRaw = new LLImageRaw;
			mStaticImageTGA->decodeAndProcess(mStaticImageRaw, info->mDomain, effective_weight);
			mNeedsCreateTexture = TRUE;			
		}

		if (mCachedProcessedTexture)
		{
			{
				// Create the GL texture, and then hang onto it for future use.
				if (mNeedsCreateTexture)
				{
					mCachedProcessedTexture->createGLTexture(0, mStaticImageRaw);
					mNeedsCreateTexture = FALSE;
					gGL.getTexUnit(0)->bind(mCachedProcessedTexture);
					mCachedProcessedTexture->setAddressMode(LLTexUnit::TAM_CLAMP);
				}

				LLGLSNoAlphaTest gls_no_alpha_test;
				gGL.getTexUnit(0)->bind(mCachedProcessedTexture);
				gl_rect_2d_simple_tex(width, height);
				gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
				stop_glerror();
			}
		}

		// Don't keep the cache for other people's avatars
		// (It's not really a "cache" in that case, but the logic is the same)
		if (!mAvatar->isSelf())
		{
			mCachedProcessedTexture = NULL;
		}
	}
	else
	{
		LLGLDisable no_alpha(GL_ALPHA_TEST);
		gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
		gGL.color4f(0.f, 0.f, 0.f, effective_weight);
		gl_rect_2d_simple(width, height);
	}

	return success;
}