#include "stdafx.h" #include "V8DataModel/SurfaceGui.h" #include "V8DataModel/DataModel.h" #include "V8DataModel/PartInstance.h" #include "Network/Player.h" #include "Network/Players.h" #include "v8datamodel/TextLabel.h" #include "GfxBase/AdornSurface.h" #include "v8datamodel/PlayerGui.h" #include "Util/RobloxGoogleAnalytics.h" FASTFLAGVARIABLE(GUIZFighterGPU, true) static void sendSurfaceGuiStats() { RBX::RobloxGoogleAnalytics::trackEvent(GA_CATEGORY_GAME, "SurfaceGui"); } static void sendSurfaceGUINonTextUsage() { RBX::RobloxGoogleAnalytics::trackEvent(GA_CATEGORY_GAME,"SurfaceGuiNonText"); } namespace RBX { int SurfaceGui::s_numInstances = 0; const char* const sAdornmentSurfaceGui = "SurfaceGui"; REFLECTION_BEGIN(); static const Reflection::RefPropDescriptor< SurfaceGui, Instance> prop_adornee("Adornee", category_Data, &SurfaceGui::getAdornee, &SurfaceGui::setAdornee); static const Reflection::PropDescriptor prop_Enabled("Enabled", category_Data, &SurfaceGui::getEnabled, &SurfaceGui::setEnabled); static const Reflection::PropDescriptor prop_Active("Active", category_Data, &SurfaceGui::getActive, &SurfaceGui::setActive); static const Reflection::PropDescriptor prop_CanvasSize( "CanvasSize", category_Data, &SurfaceGui::getCanvasSize, &SurfaceGui::setCanvasSize); static const Reflection::EnumPropDescriptor prop_Surface("Face", category_Data, &SurfaceGui::getFace, &SurfaceGui::setFace); static const Reflection::PropDescriptor prop_maxDistance("ToolPunchThroughDistance", category_Data, &SurfaceGui::getToolPunchThroughDistance, &SurfaceGui::setToolPunchThroughDistance); REFLECTION_END(); SurfaceGui::SurfaceGui() :DescribedCreatable("SurfaceGui") ,enabled(true) ,active(true) ,canvasSize(Vector2(800,600)) ,faceID(NORM_Z_NEG) ,toolPunchThroughDistance(0) { ++s_numInstances; setAbsoluteSize(canvasSize); } SurfaceGui::~SurfaceGui() { --s_numInstances; } bool SurfaceGui::askSetParent(const Instance* instance) const { return (Instance::fastDynamicCast(instance) == NULL); } bool SurfaceGui::shouldRender3dSortedAdorn() const { const shared_ptr part(getPart()); if (enabled && part) // only render if part is still in DataModel return DataModel::get(part.get()) ? true : false; else return false; } boost::shared_ptr SurfaceGui::getPart() const { boost::shared_ptr part = adornee.lock(); if(!part) { // if no adornee specified, use parent if it is a partInstance. // allows for easy insertion of billboard guis in the world that replicate and show for everyone. part = shared_from(const_cast(getParent())); } return part; } Vector3 SurfaceGui::render3dSortedPosition() const { if(boost::shared_ptr partInstance = getPart()) { if(PartInstance* thePart = fastDynamicCast(partInstance.get())) return thePart->calcRenderingCoordinateFrame().translation; } //Shouldn't be reached, but just in case. return Vector3(0,0,0); } bool SurfaceGui::buildGuiMatrix(Adorn* adorn, CoordinateFrame* partFrame, CoordinateFrame* projectionFrame) { boost::shared_ptr partInstance = getPart(); PartInstance* part = fastDynamicCast(partInstance.get()); Workspace* workspace = ServiceProvider::find(part); if (!part || !workspace) return false; CoordinateFrame cframe = part->calcRenderingCoordinateFrame(); Vector3 size = part->getPartSizeXml(); if (adorn && !adorn->isVisible(Extents::fromCenterCorner(Vector3(), size * 0.5f), cframe)) return false; static const float yaw[6] = { 90, 90, 0, -90, 90, 180 }; static const float pitch[6] = { 0, 90, 0, 0, -90, 0 }; Matrix4 rot = Matrix4::rollDegrees( pitch[faceID] ) * Matrix4::yawDegrees( yaw[faceID] ); Matrix4 vp = Matrix4::scale( 1/canvasSize.x, 1/canvasSize.y, 1 ); Matrix4 tr = Matrix4::translation( -0.5f, 0.5f, 0.5f ); Matrix4 sc = Matrix4::scale( size ); Matrix4 proj = sc * rot * tr * vp; projectionFrame->rotation = proj.upper3x3(); projectionFrame->translation = proj.column(3).xyz(); *partFrame = cframe; return true; } void SurfaceGui::render3dSortedAdorn(Adorn* adorn) { if(!enabled || isYetAnotherSpecialCase() ) return; CoordinateFrame part, proj; if (buildGuiMatrix(adorn, &part, &proj)) { AdornSurface adornView(adorn, Rect2D(canvasSize), part * proj); Super::render2d(&adornView); } } // see class def for explanation bool SurfaceGui::isYetAnotherSpecialCase() { return !!Network::Players::findLocalPlayer(this) && this->isDescendantOf( RBX::ServiceProvider::find(this) ); } void SurfaceGui::unProcess() { shared_ptr mouseMoveEvent = Creatable::create(InputObject::TYPE_MOUSEMOVEMENT, InputObject::INPUT_STATE_CANCEL,RBX::DataModel::get(this)); mouseMoveEvent->setPosition(Vector3(-1e4,-1e4,-1e4)); Super::process(mouseMoveEvent); shared_ptr focusEndEvent = Creatable::create(InputObject::TYPE_FOCUS, InputObject::INPUT_STATE_CANCEL,RBX::DataModel::get(this)); focusEndEvent->setPosition(Vector3(-1e4,-1e4,-1e4));; Super::process(focusEndEvent); } static Vector3 unproject( const CoordinateFrame& part, const CoordinateFrame& proj, Vector3 v ) { v = part.pointToObjectSpace( v ); v -= proj.translation; v = proj.rotation.inverse() * v; v.y = -v.y; return v; } GuiResponse SurfaceGui::process(const shared_ptr& event) { return GuiResponse::notSunk(); } GuiResponse SurfaceGui::process3d(const shared_ptr& event, Vector3 point3d, bool ignoreMaxDistance) { CoordinateFrame part, proj; if (!buildGuiMatrix(NULL, &part, &proj)) return GuiResponse::notSunk(); Vector3 newpoint = unproject( part, proj, point3d ); if(!enabled || !active) return GuiResponse::notSunk(); if( RBX::ModelInstance* character = Network::Players::findLocalCharacter(this) ) { Vector3 loc = character->getLocation().translation; if( !ignoreMaxDistance && (loc - point3d).length() >= toolPunchThroughDistance ) return GuiResponse::notSunk(); } if(event->isMouseEvent()) { Vector3 oldpt = event->getRawPosition(); newpoint.z = oldpt.z; event->setPosition(newpoint); GuiResponse response = Super::process(event, false); event->setPosition(oldpt); return response; } return GuiResponse::notSunk(); } bool SurfaceGui::canProcessMeAndDescendants() const { return false; } void SurfaceGui::setEnabled(bool value) { if(enabled != value) { enabled = value; raisePropertyChanged(prop_Enabled); shouldRenderSetDirty(); } } void SurfaceGui::setActive(bool value) { if(active != value) { active = value; raisePropertyChanged(prop_Active); } } void SurfaceGui::setCanvasSize( Vector2 s ) { if( canvasSize != s ) { canvasSize = s; handleResize( Rect2D(canvasSize), false ); raisePropertyChanged(prop_CanvasSize); } } ////////////////////////////////////////////////////////////////////////// struct ExpiredOrEqual { weak_ptr value; bool operator()(const weak_ptr& sg) const { return sg.expired() || weak_equal(sg, value); } }; static void removeExpiredOrEqual(std::vector< weak_ptr< SurfaceGui > >& sglist, const weak_ptr& value) { ExpiredOrEqual pred = {value}; sglist.erase(std::remove_if(sglist.begin(), sglist.end(), pred), sglist.end()); } static void removeSGFromCookie( PartInstance* pi, SurfaceGui* sg ) { if( !pi ) return; if( !pi->getSurfaceGuiCookiesRead() ) return; std::vector< weak_ptr< SurfaceGui > >* sglist = pi->getSurfaceGuiCookiesWrite(); removeExpiredOrEqual(*sglist, shared_from(sg)); } static void addSGToCookie( PartInstance* pi, SurfaceGui* sg ) { if( !pi ) return; std::vector< weak_ptr< SurfaceGui > >* sglist = pi->getSurfaceGuiCookiesWrite(); removeExpiredOrEqual(*sglist, weak_ptr()); sglist->push_back( shared_from(sg) ); } void SurfaceGui::setAdornee(Instance* value) { if(adornee.lock().get() != value) { PartInstance* oldPart = fastDynamicCast( adornee.lock().get() ); PartInstance* newPart = fastDynamicCast( value ); removeSGFromCookie(oldPart,this); addSGToCookie(newPart,this); adornee = shared_from(value); raisePropertyChanged(prop_adornee); shouldRenderSetDirty(); } } void SurfaceGui::onAncestorChanged(const AncestorChanged& event) { Super::onAncestorChanged(event); shouldRenderSetDirty(); if (Workspace::serverIsPresent(this)) { static boost::once_flag flag = BOOST_ONCE_INIT; boost::call_once(&sendSurfaceGuiStats, flag); } } void SurfaceGui::onDescendantAdded(Instance* instance) { Super::onDescendantAdded(instance); if (instance->isA()) return; if (Workspace::serverIsPresent(this)) { static boost::once_flag flag = BOOST_ONCE_INIT; boost::call_once(&sendSurfaceGUINonTextUsage, flag); } } void SurfaceGui::setFace( NormalId id ) { if( id != faceID ) { faceID = id; raisePropertyChanged(prop_Surface); } } void SurfaceGui::setToolPunchThroughDistance( float v ) { if( v != toolPunchThroughDistance ) { toolPunchThroughDistance = v; raisePropertyChanged(prop_maxDistance); } } SurfaceGui* SurfaceGui::findSurfaceGui( PartInstance* pi, NormalId surf ) { if( !pi ) { return 0; } if( const std::vector< weak_ptr< SurfaceGui > >* sglist = pi->getSurfaceGuiCookiesRead() ) { for( size_t j=0, e = sglist->size(); jgetFace() == surf && !sg->isYetAnotherSpecialCase() ) { return sg; } } } } for( size_t j=0, e = pi->numChildren(); j( pi->getChild(j) ) ) if( sg->getFace() == surf ) { return sg; } } return 0; } }