|
|
|
/*==LICENSE==*
|
|
|
|
|
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools
|
|
|
|
Copyright (C) 2011 Cyan Worlds, Inc.
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program 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 General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
Additional permissions under GNU GPL version 3 section 7
|
|
|
|
|
|
|
|
If you modify this Program, or any covered work, by linking or
|
|
|
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
|
|
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
|
|
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
|
|
|
(or a modified version of those libraries),
|
|
|
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
|
|
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
|
|
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
|
|
|
licensors of this Program grant you additional
|
|
|
|
permission to convey the resulting work. Corresponding Source for a
|
|
|
|
non-source form of such a combination shall include the source code for
|
|
|
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
|
|
|
work.
|
|
|
|
|
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
|
|
|
or by snail mail at:
|
|
|
|
Cyan Worlds, Inc.
|
|
|
|
14617 N Newport Hwy
|
|
|
|
Mead, WA 99021
|
|
|
|
|
|
|
|
*==LICENSE==*/
|
|
|
|
#include "plClothingSDLModifier.h"
|
|
|
|
#include "plAvatarClothing.h"
|
|
|
|
#include "plClothingLayout.h"
|
|
|
|
#include "plArmatureMod.h"
|
|
|
|
|
|
|
|
#include "pnSceneObject/plSceneObject.h"
|
|
|
|
#include "pnMessage/plSDLModifierMsg.h"
|
|
|
|
#include "plSDL/plSDL.h"
|
|
|
|
#include "pnKeyedObject/plKeyImp.h"
|
|
|
|
|
|
|
|
// static vars
|
|
|
|
char plClothingSDLModifier::kStrItem[]="item";
|
|
|
|
char plClothingSDLModifier::kStrTint[]="tint";
|
|
|
|
char plClothingSDLModifier::kStrTint2[]="tint2";
|
|
|
|
char plClothingSDLModifier::kStrWardrobe[]="wardrobe";
|
|
|
|
char plClothingSDLModifier::kStrSkinTint[]="skinTint";
|
|
|
|
char plClothingSDLModifier::kStrFaceBlends[]="faceBlends";
|
|
|
|
char plClothingSDLModifier::kStrAppearance[]="appearance";
|
|
|
|
char plClothingSDLModifier::kStrClothingDescName[]="clothingItem";
|
|
|
|
char plClothingSDLModifier::kStrAppearanceDescName[]="appearanceOptions";
|
|
|
|
char plClothingSDLModifier::kStrLinkInAnim[] = "linkInAnim";
|
|
|
|
|
|
|
|
//
|
|
|
|
// init latest data
|
|
|
|
//
|
|
|
|
plClothingSDLModifier::plClothingSDLModifier() :
|
|
|
|
fClothingOutfit(nil)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// find armature mod and cache it's clothingOutfit
|
|
|
|
//
|
|
|
|
plClothingOutfit* plClothingSDLModifier::GetClothingOutfit()
|
|
|
|
{
|
|
|
|
if (fClothingOutfit)
|
|
|
|
return fClothingOutfit;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
plSceneObject* target = GetTarget();
|
|
|
|
hsAssert(target, "plClothingSDLModifier: nil target");
|
|
|
|
for (i=0;i<target->GetNumModifiers();i++)
|
|
|
|
{
|
|
|
|
const plArmatureMod* am = plArmatureMod::ConvertNoRef(target->GetModifier(i));
|
|
|
|
if (am)
|
|
|
|
{
|
|
|
|
fClothingOutfit=am->GetClothingOutfit();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return fClothingOutfit;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plClothingSDLModifier::PutCurrentStateIn(plStateDataRecord* dstState)
|
|
|
|
{
|
|
|
|
IPutCurrentStateIn(dstState);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// get current state from clothingOutfit and put it in dstState
|
|
|
|
//
|
|
|
|
void plClothingSDLModifier::IPutCurrentStateIn(plStateDataRecord* dstState)
|
|
|
|
{
|
|
|
|
plSceneObject* sobj=GetTarget();
|
|
|
|
hsAssert(sobj, "plClothingSDLModifier, nil target");
|
|
|
|
|
|
|
|
plClothingOutfit* clothing = GetClothingOutfit();
|
|
|
|
hsAssert(clothing, "nil clothingOutfit");
|
|
|
|
|
|
|
|
hsTArray<plStateDataRecord*> SDRs;
|
|
|
|
hsTArray<plClothingItem*> items=clothing->GetItemList();
|
|
|
|
hsTArray<plClothingItemOptions*> options=clothing->GetOptionList();
|
|
|
|
|
|
|
|
plSDStateVariable* clothesStateDesc = dstState->FindSDVar(kStrWardrobe); // find clothes list
|
|
|
|
int itemCount=items.GetCount();
|
|
|
|
int optionsCount = options.GetCount();
|
|
|
|
hsAssert(optionsCount==itemCount, "number of items should match number of options according to clothing.sdl");
|
|
|
|
|
|
|
|
if (clothesStateDesc->GetCount() != itemCount)
|
|
|
|
clothesStateDesc->Alloc(itemCount); // set appropriate list size
|
|
|
|
|
|
|
|
int lowerCount = (itemCount <= optionsCount ? itemCount : optionsCount);
|
|
|
|
int i;
|
|
|
|
for(i = 0; i < lowerCount; i++)
|
|
|
|
{
|
|
|
|
plClosetItem closetItem;
|
|
|
|
closetItem.fItem = items[i];
|
|
|
|
closetItem.fOptions = *options[i];
|
|
|
|
|
|
|
|
PutSingleItemIntoSDR(&closetItem, clothesStateDesc->GetStateDataRecord(i));
|
|
|
|
SDRs.Append(clothesStateDesc->GetStateDataRecord(i));
|
|
|
|
}
|
|
|
|
|
|
|
|
// skin tint
|
|
|
|
plSDStateVariable* appearanceStateDesc = dstState->FindSDVar(kStrAppearance); // for skin tint
|
|
|
|
UInt8 skinTint[3];
|
|
|
|
skinTint[0] = (UInt8)(clothing->fSkinTint.r * 255);
|
|
|
|
skinTint[1] = (UInt8)(clothing->fSkinTint.g * 255);
|
|
|
|
skinTint[2] = (UInt8)(clothing->fSkinTint.b * 255);
|
|
|
|
appearanceStateDesc->GetStateDataRecord(0)->FindVar(kStrSkinTint)->Set(skinTint);
|
|
|
|
|
|
|
|
plSimpleStateVariable* faceBlends = appearanceStateDesc->GetStateDataRecord(0)->FindVar(kStrFaceBlends);
|
|
|
|
int numBlends = plClothingElement::kLayerSkinLast - plClothingElement::kLayerSkinFirst;
|
|
|
|
if (faceBlends->GetCount() != numBlends)
|
|
|
|
faceBlends->Alloc(numBlends);
|
|
|
|
for(i = 0; i < numBlends; i++)
|
|
|
|
faceBlends->Set((UInt8)(clothing->fSkinBlends[i] * 255), i);
|
|
|
|
|
|
|
|
SDRs.Append(appearanceStateDesc->GetStateDataRecord(0));
|
|
|
|
|
|
|
|
// This logically belongs in the avatar.sdl file, but clothing.sdl is already broadcast to
|
|
|
|
// other players when you join an age, and I don't want to broadcast all of avatar.sdl for
|
|
|
|
// just this one value.
|
|
|
|
plSimpleStateVariable *var = dstState->FindVar(kStrLinkInAnim);
|
|
|
|
if (var)
|
|
|
|
var->Set(clothing->fAvatar->fLinkInAnimKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plClothingSDLModifier::PutSingleItemIntoSDR(plClosetItem *item, plStateDataRecord *sdr)
|
|
|
|
{
|
|
|
|
plKey key = item->fItem->GetKey();
|
|
|
|
sdr->FindVar(kStrItem)->Set(key);
|
|
|
|
|
|
|
|
//hsColorRGBA c = item->fOptions.fTint1;
|
|
|
|
//hsColorRGBA c2 = item->fOptions.fTint2;
|
|
|
|
UInt8 c[3];
|
|
|
|
UInt8 c2[3];
|
|
|
|
c[0] = (UInt8)(item->fOptions.fTint1.r * 255);
|
|
|
|
c[1] = (UInt8)(item->fOptions.fTint1.g * 255);
|
|
|
|
c[2] = (UInt8)(item->fOptions.fTint1.b * 255);
|
|
|
|
c2[0] = (UInt8)(item->fOptions.fTint2.r * 255);
|
|
|
|
c2[1] = (UInt8)(item->fOptions.fTint2.g * 255);
|
|
|
|
c2[2] = (UInt8)(item->fOptions.fTint2.b * 255);
|
|
|
|
|
|
|
|
sdr->FindVar(kStrTint)->Set(c);
|
|
|
|
sdr->FindVar(kStrTint2)->Set(c2);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Apply the SDL state that is passed in to the current clothing state.
|
|
|
|
//
|
|
|
|
void plClothingSDLModifier::ISetCurrentStateFrom(const plStateDataRecord* srcState)
|
|
|
|
{
|
|
|
|
plSceneObject* sobj=GetTarget();
|
|
|
|
hsAssert(sobj, "plClothingSDLModifier, nil target");
|
|
|
|
|
|
|
|
plClothingOutfit* clothing = GetClothingOutfit();
|
|
|
|
if( clothing == nil )
|
|
|
|
{
|
|
|
|
hsAssert(clothing, "nil clothingOutfit");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
plSDStateVariable* clothesStateDesc = srcState->FindSDVar(kStrWardrobe); // find clothes list
|
|
|
|
int num=clothesStateDesc->GetCount(); // size of clothes list
|
|
|
|
bool update=true;
|
|
|
|
|
|
|
|
// We just remove the accessories. Any regular items will be replaced
|
|
|
|
// when we try to wear something else in the same category. (And in
|
|
|
|
// case the player lies, this guarantees they'll at least be wearing something.)
|
|
|
|
clothing->StripAccessories();
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for(i=0;i<num;i++)
|
|
|
|
{
|
|
|
|
// get clothes item from clothes list
|
|
|
|
plStateDataRecord* clothingItemState=clothesStateDesc->GetStateDataRecord(i);
|
|
|
|
HandleSingleSDR(clothingItemState, clothing);
|
|
|
|
}
|
|
|
|
|
|
|
|
plSDStateVariable* appearanceStateDesc = srcState->FindSDVar(kStrAppearance); // for skin tint
|
|
|
|
plStateDataRecord* appearanceState = appearanceStateDesc->GetStateDataRecord(0);
|
|
|
|
HandleSingleSDR(appearanceState, clothing);
|
|
|
|
|
|
|
|
if (update)
|
|
|
|
clothing->ForceUpdate(true /* retry */);
|
|
|
|
|
|
|
|
plSimpleStateVariable *var;
|
|
|
|
var = srcState->FindVar(kStrLinkInAnim);
|
|
|
|
if (var)
|
|
|
|
var->Get(&clothing->fAvatar->fLinkInAnimKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plClothingSDLModifier::HandleSingleSDR(const plStateDataRecord *sdr, plClothingOutfit *clothing /* = nil */, plClosetItem *closetItem /* = nil */)
|
|
|
|
{
|
|
|
|
if (sdr == nil)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
UInt8 tint[3];
|
|
|
|
hsScalar tintScalar[3];
|
|
|
|
if (!strcmp(sdr->GetDescriptor()->GetName(), kStrClothingDescName))
|
|
|
|
{
|
|
|
|
// get item from clothesItem
|
|
|
|
plSimpleStateVariable* itemVar = sdr->FindVar(kStrItem);
|
|
|
|
plClothingItem* clothingItem = nil; // clothing->GetItemList()[i];
|
|
|
|
if (itemVar->IsUsed())
|
|
|
|
{
|
|
|
|
plKey key;
|
|
|
|
itemVar->Get(&key);
|
|
|
|
clothingItem = plClothingItem::ConvertNoRef(key ? key->GetObjectPtr() : nil);
|
|
|
|
if (clothingItem)
|
|
|
|
{
|
|
|
|
if (clothing)
|
|
|
|
clothing->AddItem(clothingItem, false, false /*bcast */);
|
|
|
|
if (closetItem)
|
|
|
|
closetItem->fItem = clothingItem;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// tints
|
|
|
|
if (clothingItem)
|
|
|
|
{
|
|
|
|
// get item from clothesItem
|
|
|
|
plSimpleStateVariable* tintVar = sdr->FindVar(kStrTint);
|
|
|
|
if (tintVar->IsUsed())
|
|
|
|
{
|
|
|
|
tintVar->Get(tint);
|
|
|
|
tintScalar[0] = tint[0] / 255.f;
|
|
|
|
tintScalar[1] = tint[1] / 255.f;
|
|
|
|
tintScalar[2] = tint[2] / 255.f;
|
|
|
|
if (clothing)
|
|
|
|
clothing->TintItem(clothingItem, tintScalar[0], tintScalar[1], tintScalar[2],
|
|
|
|
false /*update*/, false /*broadcast*/, false /*netForce*/, true, plClothingElement::kLayerTint1);
|
|
|
|
if (closetItem)
|
|
|
|
closetItem->fOptions.fTint1.Set(tintScalar[0], tintScalar[1], tintScalar[2], 1.f);
|
|
|
|
}
|
|
|
|
|
|
|
|
tintVar=sdr->FindVar(kStrTint2);
|
|
|
|
if (tintVar->IsUsed())
|
|
|
|
{
|
|
|
|
tintVar->Get(tint);
|
|
|
|
tintScalar[0] = tint[0] / 255.f;
|
|
|
|
tintScalar[1] = tint[1] / 255.f;
|
|
|
|
tintScalar[2] = tint[2] / 255.f;
|
|
|
|
|
|
|
|
if (clothing)
|
|
|
|
clothing->TintItem(clothingItem, tintScalar[0], tintScalar[1], tintScalar[2],
|
|
|
|
false /*update*/, false /*broadcast*/, false /*netForce*/, true, plClothingElement::kLayerTint2);
|
|
|
|
if (closetItem)
|
|
|
|
closetItem->fOptions.fTint2.Set(tintScalar[0], tintScalar[1], tintScalar[2], 1.f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (!strcmp(sdr->GetDescriptor()->GetName(), kStrAppearanceDescName))
|
|
|
|
{
|
|
|
|
// skin tints
|
|
|
|
plSimpleStateVariable* skinVar = sdr->FindVar(kStrSkinTint);
|
|
|
|
if (skinVar->IsUsed())
|
|
|
|
{
|
|
|
|
skinVar->Get(tint);
|
|
|
|
if (clothing)
|
|
|
|
clothing->TintSkin(tint[0] / 255.f, tint[1] / 255.f, tint[2] / 255.f, false /* update */, false /*broadcast*/);
|
|
|
|
}
|
|
|
|
plSimpleStateVariable* faceBlends = sdr->FindVar(kStrFaceBlends);
|
|
|
|
if (faceBlends->IsUsed())
|
|
|
|
{
|
|
|
|
int numBlends = plClothingElement::kLayerSkinLast - plClothingElement::kLayerSkinFirst;
|
|
|
|
for(i = 0; i < numBlends && i < faceBlends->GetCount(); i++)
|
|
|
|
{
|
|
|
|
UInt8 blend;
|
|
|
|
faceBlends->Get(&blend, i);
|
|
|
|
clothing->fSkinBlends[i] = (hsScalar)blend / 255;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// FINDARMATUREMOD
|
|
|
|
const plClothingSDLModifier *plClothingSDLModifier::FindClothingSDLModifier(const plSceneObject *obj)
|
|
|
|
{
|
|
|
|
int count = obj->GetNumModifiers();
|
|
|
|
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
const plModifier *mod = obj->GetModifier(i);
|
|
|
|
const plClothingSDLModifier *sdlMod = plClothingSDLModifier::ConvertNoRef(mod);
|
|
|
|
if(sdlMod)
|
|
|
|
return sdlMod;
|
|
|
|
}
|
|
|
|
return nil;
|
|
|
|
}
|