Files
2025-09-18 17:55:52 -04:00

371 lines
10 KiB
C++

#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<SurfaceGui, bool> prop_Enabled("Enabled", category_Data, &SurfaceGui::getEnabled, &SurfaceGui::setEnabled);
static const Reflection::PropDescriptor<SurfaceGui, bool> prop_Active("Active", category_Data, &SurfaceGui::getActive, &SurfaceGui::setActive);
static const Reflection::PropDescriptor<SurfaceGui, Vector2> prop_CanvasSize( "CanvasSize", category_Data, &SurfaceGui::getCanvasSize, &SurfaceGui::setCanvasSize);
static const Reflection::EnumPropDescriptor<SurfaceGui, NormalId > prop_Surface("Face", category_Data, &SurfaceGui::getFace, &SurfaceGui::setFace);
static const Reflection::PropDescriptor<SurfaceGui, float> prop_maxDistance("ToolPunchThroughDistance", category_Data, &SurfaceGui::getToolPunchThroughDistance, &SurfaceGui::setToolPunchThroughDistance);
REFLECTION_END();
SurfaceGui::SurfaceGui()
:DescribedCreatable<SurfaceGui, GuiLayerCollector, sAdornmentSurfaceGui>("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<GuiBase2d>(instance) == NULL);
}
bool SurfaceGui::shouldRender3dSortedAdorn() const
{
const shared_ptr<Instance> 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<Instance> SurfaceGui::getPart() const
{
boost::shared_ptr<Instance> 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<Instance*>(getParent()));
}
return part;
}
Vector3 SurfaceGui::render3dSortedPosition() const
{
if(boost::shared_ptr<Instance> partInstance = getPart())
{
if(PartInstance* thePart = fastDynamicCast<PartInstance>(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<Instance> partInstance = getPart();
PartInstance* part = fastDynamicCast<PartInstance>(partInstance.get());
Workspace* workspace = ServiceProvider::find<Workspace>(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<RBX::StarterGuiService>(this) );
}
void SurfaceGui::unProcess()
{
shared_ptr<InputObject> mouseMoveEvent = Creatable<Instance>::create<InputObject>(InputObject::TYPE_MOUSEMOVEMENT, InputObject::INPUT_STATE_CANCEL,RBX::DataModel::get(this));
mouseMoveEvent->setPosition(Vector3(-1e4,-1e4,-1e4));
Super::process(mouseMoveEvent);
shared_ptr<InputObject> focusEndEvent = Creatable<Instance>::create<InputObject>(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<InputObject>& event)
{
return GuiResponse::notSunk();
}
GuiResponse SurfaceGui::process3d(const shared_ptr<InputObject>& 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<SurfaceGui> value;
bool operator()(const weak_ptr<SurfaceGui>& sg) const
{
return sg.expired() || weak_equal(sg, value);
}
};
static void removeExpiredOrEqual(std::vector< weak_ptr< SurfaceGui > >& sglist, const weak_ptr<SurfaceGui>& 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<SurfaceGui>());
sglist->push_back( shared_from(sg) );
}
void SurfaceGui::setAdornee(Instance* value)
{
if(adornee.lock().get() != value)
{
PartInstance* oldPart = fastDynamicCast<PartInstance>( adornee.lock().get() );
PartInstance* newPart = fastDynamicCast<PartInstance>( 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<TextLabel>()) 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(); j<e; ++j )
{
if( SurfaceGui* sg = (*sglist)[j].lock().get() )
{
if( sg->getFace() == surf && !sg->isYetAnotherSpecialCase() )
{
return sg;
}
}
}
}
for( size_t j=0, e = pi->numChildren(); j<e; ++j )
{
if( SurfaceGui* sg = fastDynamicCast<SurfaceGui>( pi->getChild(j) ) )
if( sg->getFace() == surf )
{
return sg;
}
}
return 0;
}
}