/*==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 . 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 "hsTypes.h" #include "plLayerInterface.h" #include "plMessage/plLayRefMsg.h" #include "plLayer.h" #include "hsMatrix44.h" #include "hsGMatState.h" #include "hsResMgr.h" #include "pnNetCommon/plSDLTypes.h" plLayerInterface::plLayerInterface() : fUnderLay(nil), fOverLay(nil), fState(nil), fTransform(nil), fPreshadeColor(nil), fRuntimeColor(nil), fAmbientColor(nil), fOpacity(nil), fTexture(nil), fUVWSrc(nil), fLODBias(nil), fSpecularColor(nil), fSpecularPower(nil), fOwnedChannels(0), fPassThruChannels(0), fVertexShader(nil), fPixelShader(nil), fBumpEnvXfm(nil) { } plLayerInterface::~plLayerInterface() { if( fUnderLay ) Detach(fUnderLay); delete fState; delete fPreshadeColor; delete fRuntimeColor; delete fAmbientColor; delete fSpecularColor; delete fOpacity; delete fTransform; delete fTexture; delete fUVWSrc; delete fLODBias; delete fSpecularPower; delete fVertexShader; delete fPixelShader; delete fBumpEnvXfm; } void plLayerInterface::ISetPassThru(UInt32 chans) { fPassThruChannels |= chans; if( fOverLay ) fOverLay->ISetPassThru(chans); // Since plLayerAnimation is the only derived class that uses its // fPassThruChannels info, it's the only one that actually saves // it to state. DirtySynchState(kSDLLayer, 0); } // The arbitration rules for different layers on the same stack // wanting to control the same channel are currently: // 1) Only one write-only value setter can be active at a time, // otherwise results are undefined. // 2) A layer will only become active due to receiving a message. // 3) A channel value for the stack is the value as set by the // last layer that was active. If no layers have ever been // active, the value is the static value of the bottom of the stack. // 4) Since the stack is only Eval'd when visible, the third rule // must appear to be true when different layers become active // and inactive without ever having been Eval'd. // 5) Taking advantage of rules 1) and 2), it follows that the last // layer to have become active on response to a message is also // the last layer to have been active. // 6) So when a layer becomes active in it's MsgReceive(), it notifies // all channels above it that it now owns its channels, and they // should just pass through those channel values. // Note that a layer may claim ownership of its channels but then lose // ownership (because another layer went active) before ever having // been Eval'd. void plLayerInterface::ClaimChannels(UInt32 chans) { if( fOverLay ) fOverLay->ISetPassThru(chans); fPassThruChannels &= ~chans; DirtySynchState(kSDLLayer, 0); } UInt32 plLayerInterface::Eval(double secs, UInt32 frame, UInt32 ignore) { if( fUnderLay ) return fUnderLay->Eval(secs, frame, ignore); return UInt32(0); } // Export Only void plLayerInterface::AttachViaNotify(plLayerInterface *prev) { plLayRefMsg* refMsg = TRACKED_NEW plLayRefMsg(GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kUnderLay); hsgResMgr::ResMgr()->AddViaNotify(prev->GetKey(), refMsg, plRefFlags::kActiveRef); } plLayerInterface* plLayerInterface::Attach(plLayerInterface* prev) { if( !prev ) return this; if( fUnderLay == prev ) return this; if( fUnderLay ) { fUnderLay->Attach(prev); prev = fUnderLay; } if( !OwnChannel(kState) ) fState = prev->fState; if( !OwnChannel(kPreshadeColor) ) fPreshadeColor = prev->fPreshadeColor; if( !OwnChannel( kRuntimeColor ) ) fRuntimeColor = prev->fRuntimeColor; if( !OwnChannel(kAmbientColor) ) fAmbientColor = prev->fAmbientColor; if( !OwnChannel( kSpecularColor ) ) fSpecularColor = prev->fSpecularColor; if( !OwnChannel(kOpacity) ) fOpacity = prev->fOpacity; if( !OwnChannel(kTransform) ) fTransform = prev->fTransform; if( !OwnChannel(kTexture) ) fTexture = prev->fTexture; if( !OwnChannel(kUVWSrc) ) fUVWSrc = prev->fUVWSrc; if( !OwnChannel(kLODBias) ) fLODBias = prev->fLODBias; if( !OwnChannel(kSpecularPower) ) fSpecularPower = prev->fSpecularPower; if( !OwnChannel(kVertexShader) ) fVertexShader = prev->fVertexShader; if( !OwnChannel(kPixelShader) ) fPixelShader = prev->fPixelShader; if( !OwnChannel(kBumpEnvXfm) ) fBumpEnvXfm = prev->fBumpEnvXfm; fUnderLay = prev; prev->fOverLay = this; return this; } void plLayerInterface::IUnthread() { if( fUnderLay ) { if( !OwnChannel(kState) ) fState = nil; if( !OwnChannel(kPreshadeColor) ) fPreshadeColor = nil; if( !OwnChannel( kRuntimeColor ) ) fRuntimeColor = nil; if( !OwnChannel(kAmbientColor) ) fAmbientColor = nil; if( !OwnChannel( kSpecularColor ) ) fSpecularColor = nil; if( !OwnChannel(kOpacity) ) fOpacity = nil; if( !OwnChannel(kTransform) ) fTransform = nil; if( !OwnChannel(kTexture) ) fTexture = nil; if( !OwnChannel(kUVWSrc) ) fUVWSrc = nil; if( !OwnChannel(kLODBias) ) fLODBias = nil; if( !OwnChannel(kSpecularPower) ) fSpecularPower = nil; if( !OwnChannel(kVertexShader) ) fVertexShader = nil; if( !OwnChannel(kPixelShader) ) fPixelShader = nil; if( !OwnChannel(kBumpEnvXfm) ) fBumpEnvXfm = nil; fUnderLay->fOverLay = nil; fUnderLay = nil; } } // Detach: // If we are the one being detached, break our links to underlay // and then return nil, since everything has just been detached // from the stack. // If our underlay is the one being detached, we need to unthread from it // and return ourselves. // If it's not us, and not our underlay, just pass it to our underlay and let // it deal. // // Return value is new TOP of stack. li is now top of a separate stack. plLayerInterface* plLayerInterface::Detach(plLayerInterface* li) { if( li == this ) return nil; if( li == fUnderLay ) { IUnthread(); return this; } fUnderLay->Detach(li); return this; } // Remove: // If we are the one being removed, break our links to underlay // and then just return underlay, since it doesn't even know // about our existence (so it doesn't need to know about the remove). // If our underlay is the one being removed, we need to unthread it from // its underlay (if any), and then thread ourselves onto the underlay's // former underlay. // If it's not us, and not our underlay, just pass it to our underlay and let // it deal. // // Return value is new TOP of stack. plLayerInterface* plLayerInterface::Remove(plLayerInterface* li) { plLayerInterface* under = fUnderLay; if( li == this ) { IUnthread(); return under; } // This is an error, because it means we're being asked // to detach from something we aren't attached to. if( !under ) { hsAssert(false, "Detaching from unknown layerinterface"); return this; } IUnthread(); plLayerInterface* newUnderLay = under->Remove(li); Attach(newUnderLay); return this; } plLayerInterface *plLayerInterface::GetAttached() { return fUnderLay; } void plLayerInterface::Read(hsStream* s, hsResMgr* mgr) { plSynchedObject::Read(s, mgr); plLayRefMsg* refMsg = TRACKED_NEW plLayRefMsg(GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kUnderLay); plKey key = mgr->ReadKeyNotifyMe(s,refMsg, plRefFlags::kActiveRef); if( key && !fUnderLay ) Attach(plLayer::DefaultLayer()); // Temporary setting default netgroup by our key. SetNetGroup(SelectNetGroup(GetKey())); } void plLayerInterface::Write(hsStream* s, hsResMgr* mgr) { plSynchedObject::Write(s, mgr); mgr->WriteKey(s, fUnderLay); } hsBool plLayerInterface::MsgReceive(plMessage* msg) { plLayRefMsg* refMsg = plLayRefMsg::ConvertNoRef(msg); if( refMsg ) { switch( refMsg->fType ) { case plLayRefMsg::kUnderLay: { plLayerInterface* underLay = plLayerInterface::ConvertNoRef(refMsg->GetRef()); if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) { if( fUnderLay ) Detach(fUnderLay); Attach(underLay); } else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) { Detach(fUnderLay); } return true; } } } return plSynchedObject::MsgReceive(msg); }