forked from aya/aya
Initial commit
This commit is contained in:
1365
engine/app/CMakeLists.txt
Normal file
1365
engine/app/CMakeLists.txt
Normal file
File diff suppressed because it is too large
Load Diff
806
engine/app/src/DataModel/Accoutrement.cpp
Normal file
806
engine/app/src/DataModel/Accoutrement.cpp
Normal file
@@ -0,0 +1,806 @@
|
||||
|
||||
|
||||
|
||||
|
||||
#include "DataModel/Accoutrement.hpp"
|
||||
#include "DataModel/JointInstance.hpp"
|
||||
#include "DataModel/Workspace.hpp"
|
||||
#include "DataModel/DataModel.hpp"
|
||||
#include "DataModel/Attachment.hpp"
|
||||
#include "Humanoid/Humanoid.hpp"
|
||||
#include "Players.hpp"
|
||||
#include "Player.hpp"
|
||||
#include "Tool/DragUtilities.hpp"
|
||||
#include "Script/script.hpp"
|
||||
|
||||
DYNAMIC_FASTFLAGVARIABLE(AccessoriesAndAttachments, false)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
using namespace Aya::Network;
|
||||
|
||||
const char* const sAccoutrement = "Accoutrement";
|
||||
const char* const sHat = "Hat";
|
||||
const char* const sAccessory = "Accessory";
|
||||
|
||||
|
||||
static const Reflection::PropDescriptor<Accoutrement, CoordinateFrame> prop_AttachmentPoint("AttachmentPoint", category_Appearance,
|
||||
&Accoutrement::getAttachmentPoint, &Accoutrement::setAttachmentPoint, Reflection::PropertyDescriptor::STANDARD);
|
||||
static const Reflection::PropDescriptor<Accoutrement, Vector3> prop_AttachmentPos(
|
||||
"AttachmentPos", category_Appearance, &Accoutrement::getAttachmentPos, &Accoutrement::setAttachmentPos, Reflection::PropertyDescriptor::UI);
|
||||
static const Reflection::PropDescriptor<Accoutrement, Vector3> prop_AttachmentForward("AttachmentForward", category_Appearance,
|
||||
&Accoutrement::getAttachmentForward, &Accoutrement::setAttachmentForward, Reflection::PropertyDescriptor::UI);
|
||||
static const Reflection::PropDescriptor<Accoutrement, Vector3> prop_AttachmentUp(
|
||||
"AttachmentUp", category_Appearance, &Accoutrement::getAttachmentUp, &Accoutrement::setAttachmentUp, Reflection::PropertyDescriptor::UI);
|
||||
static const Reflection::PropDescriptor<Accoutrement, Vector3> prop_AttachmentRight(
|
||||
"AttachmentRight", category_Appearance, &Accoutrement::getAttachmentRight, &Accoutrement::setAttachmentRight, Reflection::PropertyDescriptor::UI);
|
||||
|
||||
static const Reflection::PropDescriptor<Accoutrement, int> prop_BackendAccoutrementState("BackendAccoutrementState", category_Appearance,
|
||||
&Accoutrement::getBackendAccoutrementState, &Accoutrement::setBackendAccoutrementState, Reflection::PropertyDescriptor::REPLICATE_ONLY);
|
||||
|
||||
REFLECTION_END();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
// FRONTEND AND BACKEND
|
||||
|
||||
Accoutrement::Accoutrement()
|
||||
: DescribedCreatable<Accoutrement, Instance, sAccoutrement>()
|
||||
, backendAccoutrementState(NOTHING)
|
||||
, handleTouched(FLog::TouchedSignal)
|
||||
{
|
||||
setName(sAccoutrement);
|
||||
}
|
||||
|
||||
|
||||
Accoutrement::~Accoutrement()
|
||||
{
|
||||
AYAASSERT(!weld);
|
||||
AYAASSERT(!handleTouched.connected());
|
||||
AYAASSERT(!characterChildAdded.connected());
|
||||
AYAASSERT(!characterChildRemoved.connected());
|
||||
}
|
||||
|
||||
void Accoutrement::onCameraNear(float distance)
|
||||
{
|
||||
for (size_t i = 0; i < numChildren(); i++)
|
||||
{
|
||||
if (CameraSubject* cameraSubject = queryTypedChild<CameraSubject>(i))
|
||||
{
|
||||
cameraSubject->onCameraNear(distance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Accoutrement::render3dSelect(Adorn* adorn, SelectState selectState)
|
||||
{
|
||||
for (size_t i = 0; i < this->numChildren(); ++i)
|
||||
{
|
||||
if (IAdornable* iRenderable = dynamic_cast<IAdornable*>(this->getChild(i)))
|
||||
{
|
||||
iRenderable->render3dSelect(adorn, selectState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Accoutrement::dropAll(ModelInstance* character)
|
||||
{
|
||||
Accoutrement::dropAllOthers(character, NULL);
|
||||
}
|
||||
|
||||
void Accoutrement::dropAllOthers(ModelInstance* character, Accoutrement* exception)
|
||||
{
|
||||
if (Workspace* workspace = Workspace::findWorkspace(character))
|
||||
{
|
||||
while (Accoutrement* acc = character->findFirstChildOfType<Accoutrement>())
|
||||
{
|
||||
if (acc != exception)
|
||||
acc->setParent(workspace); // ok - should drop! check it out
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PartInstance* Accoutrement::getHandle()
|
||||
{
|
||||
return const_cast<PartInstance*>(getHandleConst());
|
||||
}
|
||||
|
||||
const PartInstance* Accoutrement::getHandleConst() const
|
||||
{
|
||||
return Instance::fastDynamicCast<PartInstance>(findConstFirstChildByName("Handle"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
Attachment* Accoutrement::findFirstMatchingAttachment(Instance* model, const std::string& originalAttachmentName)
|
||||
{
|
||||
if (model != NULL)
|
||||
{
|
||||
if (shared_ptr<const Instances> children = model->getChildren2())
|
||||
{
|
||||
for (Instances::const_iterator childIterator = children->begin(); childIterator != children->end(); ++childIterator)
|
||||
{
|
||||
Instance* child = (*childIterator).get();
|
||||
Attachment* attachment = Instance::fastDynamicCast<Attachment>(child);
|
||||
// check to see if child is an attachment and if the attachment fits with our original attachment
|
||||
if ((attachment != NULL) && (attachment->getName() == originalAttachmentName))
|
||||
{
|
||||
return attachment;
|
||||
}
|
||||
|
||||
// Continue recursive search but ignore accourtements/accessories
|
||||
if (Instance::fastDynamicCast<Accoutrement>(child) == NULL)
|
||||
{
|
||||
Attachment* foundAttachment = Accoutrement::findFirstMatchingAttachment(child, originalAttachmentName);
|
||||
// If the deeper search found one then keep handign it up, otherwise continue searching horizontally
|
||||
if (foundAttachment != NULL)
|
||||
{
|
||||
return foundAttachment;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
const CoordinateFrame Accoutrement::getLocation()
|
||||
{
|
||||
const PartInstance* handle = getHandleConst();
|
||||
|
||||
return handle ? handle->getCoordinateFrame() : CoordinateFrame();
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// REPLICATION - Front and Back end
|
||||
|
||||
|
||||
|
||||
/*
|
||||
watch_touch
|
||||
|
||||
IN_CHARACTER x
|
||||
IN_WORKSPACE x -> needs touch or parent already set to proceed
|
||||
HAS_HANDLE x
|
||||
NOTHING
|
||||
*/
|
||||
|
||||
void Accoutrement::setBackendAccoutrementStateNoReplicate(int value)
|
||||
{
|
||||
backendAccoutrementState = static_cast<AccoutrementState>(value);
|
||||
}
|
||||
|
||||
|
||||
void Accoutrement::setBackendAccoutrementState(int value)
|
||||
{
|
||||
if (value != backendAccoutrementState)
|
||||
{
|
||||
setBackendAccoutrementStateNoReplicate(value);
|
||||
|
||||
raisePropertyChanged(prop_BackendAccoutrementState);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
// BACKEND
|
||||
|
||||
void Accoutrement::connectTouchEvent()
|
||||
{
|
||||
PartInstance* handle = getHandle();
|
||||
|
||||
if (handle)
|
||||
{
|
||||
handleTouched = handle->onDemandWrite()->touchedSignal.connect(boost::bind(&Accoutrement::onEvent_HandleTouched, this, _1));
|
||||
FASTLOG3(FLog::TouchedSignal, "Connecting Accoutrement to touched signal, instance: %p, part: %p, part signal: %p", this, handle,
|
||||
&(handle->onDemandRead()->touchedSignal));
|
||||
}
|
||||
else
|
||||
{
|
||||
// AYAASSERT(0); // only call if >= the HAS_HANDLE state
|
||||
handleTouched.disconnect(); // just to be safe
|
||||
}
|
||||
}
|
||||
|
||||
void Accoutrement::connectAttachmentAdjustedEvent()
|
||||
{
|
||||
attachmentAdjusted.disconnect();
|
||||
if (PartInstance* handle = getHandle())
|
||||
{
|
||||
if (Attachment* attachment = handle->findFirstChildOfType<Attachment>())
|
||||
{
|
||||
attachmentAdjusted = attachment->propertyChangedSignal.connect(boost::bind(&Accoutrement::onEvent_AttachmentAdjusted, this, _1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Accoutrement::rebuildBackendState()
|
||||
{
|
||||
AccoutrementState desiredState = computeDesiredState();
|
||||
|
||||
const ServiceProvider* serviceProvider = ServiceProvider::findServiceProvider(this);
|
||||
AYAASSERT(serviceProvider);
|
||||
|
||||
setDesiredState(desiredState, serviceProvider);
|
||||
}
|
||||
|
||||
/*
|
||||
Backend:
|
||||
watch_touch
|
||||
IN_CHARACTER x
|
||||
IN_WORKSPACE x -> needs touch or parent already set to proceed
|
||||
HAS_HANDLE x
|
||||
NOTHING
|
||||
*/
|
||||
|
||||
Accoutrement::AccoutrementState Accoutrement::computeDesiredState()
|
||||
{
|
||||
AYAASSERT(Network::Players::backendProcessing(this));
|
||||
|
||||
PartInstance* handle = getHandle();
|
||||
if (!handle)
|
||||
{
|
||||
return NOTHING;
|
||||
}
|
||||
|
||||
if (!Workspace::contextInWorkspace(this))
|
||||
{
|
||||
return HAS_HANDLE;
|
||||
}
|
||||
|
||||
return computeDesiredState(getParent());
|
||||
}
|
||||
|
||||
Accoutrement::AccoutrementState Accoutrement::computeDesiredState(Instance* testParent)
|
||||
{
|
||||
|
||||
Humanoid* humanoid = Humanoid::modelIsCharacter(testParent);
|
||||
|
||||
// Parent is not a character model, we are in the workspace
|
||||
if (!humanoid)
|
||||
{
|
||||
return IN_WORKSPACE;
|
||||
}
|
||||
// make sure we are not already wearing a hat
|
||||
/*if (testParent->findFirstChildOfType<Accoutrement>() != NULL)
|
||||
{
|
||||
return IN_WORKSPACE;
|
||||
}*/
|
||||
|
||||
if (humanoid->getTorsoSlow())
|
||||
{
|
||||
return EQUIPPED;
|
||||
}
|
||||
|
||||
return NOTHING;
|
||||
}
|
||||
|
||||
|
||||
void Accoutrement::setDesiredState(AccoutrementState desiredState, const ServiceProvider* serviceProvider)
|
||||
{
|
||||
AYAASSERT(Network::Players::backendProcessing(serviceProvider));
|
||||
|
||||
if (desiredState > backendAccoutrementState)
|
||||
{
|
||||
switch (backendAccoutrementState)
|
||||
{
|
||||
case IN_CHARACTER:
|
||||
upTo_Equipped();
|
||||
break;
|
||||
case IN_WORKSPACE:
|
||||
upTo_InCharacter();
|
||||
break;
|
||||
case HAS_HANDLE:
|
||||
upTo_InWorkspace();
|
||||
break;
|
||||
case NOTHING:
|
||||
upTo_HasHandle();
|
||||
break;
|
||||
default:
|
||||
AYAASSERT(0);
|
||||
}
|
||||
setBackendAccoutrementState(static_cast<AccoutrementState>(backendAccoutrementState + 1));
|
||||
}
|
||||
else if (desiredState < backendAccoutrementState)
|
||||
{
|
||||
switch (backendAccoutrementState)
|
||||
{
|
||||
case EQUIPPED:
|
||||
downFrom_Equipped();
|
||||
break;
|
||||
case IN_CHARACTER:
|
||||
downFrom_InCharacter();
|
||||
break;
|
||||
case IN_WORKSPACE:
|
||||
downFrom_InWorkspace();
|
||||
break;
|
||||
case HAS_HANDLE:
|
||||
downFrom_HasHandle();
|
||||
break;
|
||||
default:
|
||||
AYAASSERT(0);
|
||||
}
|
||||
setBackendAccoutrementState(static_cast<AccoutrementState>(backendAccoutrementState - 1));
|
||||
}
|
||||
// do again if still not there - recursive
|
||||
if (desiredState != backendAccoutrementState)
|
||||
{
|
||||
setDesiredState(desiredState, serviceProvider);
|
||||
}
|
||||
}
|
||||
|
||||
void Accoutrement::upTo_HasHandle()
|
||||
{
|
||||
connectTouchEvent();
|
||||
}
|
||||
|
||||
void Accoutrement::downFrom_HasHandle()
|
||||
{
|
||||
handleTouched.disconnect();
|
||||
if (DFFlag::AccessoriesAndAttachments)
|
||||
{
|
||||
attachmentAdjusted.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
void Accoutrement::upTo_InWorkspace()
|
||||
{
|
||||
AYAASSERT(ServiceProvider::findServiceProvider(this));
|
||||
AYAASSERT(Network::Players::backendProcessing(this));
|
||||
}
|
||||
|
||||
void Accoutrement::downFrom_InWorkspace() {}
|
||||
|
||||
void Accoutrement::upTo_InCharacter()
|
||||
{
|
||||
AYAASSERT(Humanoid::modelIsCharacter(getParent()));
|
||||
|
||||
characterChildAdded = getParent()->onDemandWrite()->childAddedSignal.connect(boost::bind(&Accoutrement::onEvent_AddedBackend, this, _1));
|
||||
characterChildRemoved = getParent()->onDemandWrite()->childRemovedSignal.connect(boost::bind(&Accoutrement::onEvent_RemovedBackend, this, _1));
|
||||
}
|
||||
|
||||
void Accoutrement::downFrom_InCharacter()
|
||||
{
|
||||
characterChildAdded.disconnect();
|
||||
characterChildRemoved.disconnect();
|
||||
}
|
||||
|
||||
void Accoutrement::UnequipThis(shared_ptr<Instance> instance)
|
||||
{
|
||||
// if this is an accoutrement that is equipped, unequip
|
||||
Accoutrement* a = Instance::fastDynamicCast<Accoutrement>(instance.get());
|
||||
if (!a)
|
||||
return;
|
||||
if (a->backendAccoutrementState == EQUIPPED)
|
||||
{
|
||||
a->setDesiredState(IN_WORKSPACE, ServiceProvider::findServiceProvider(a));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This code is the nearly the same as Tool::upTo_Equipped. That's evil!
|
||||
void Accoutrement::upTo_Equipped()
|
||||
{
|
||||
// Unequip other hats
|
||||
/*
|
||||
Instance *i = dynamic_cast<Instance *>(getParent());
|
||||
AYAASSERT(i);
|
||||
if (i->getChildren())
|
||||
std::for_each(
|
||||
i->getChildren()->begin(),
|
||||
i->getChildren()->end(),
|
||||
boost::bind(&Accoutrement::UnequipThis, _1)
|
||||
);
|
||||
*/
|
||||
|
||||
// Disconnect listening for touches
|
||||
handleTouched.disconnect();
|
||||
|
||||
// Unjoin tool from other parts if any remaining joints
|
||||
PartArray parts;
|
||||
PartInstance::findParts(this, parts);
|
||||
DragUtilities::unJoinFromOutsiders(parts);
|
||||
|
||||
Humanoid* humanoid = Humanoid::modelIsCharacter(getParent());
|
||||
if (!humanoid)
|
||||
return;
|
||||
|
||||
if (DFFlag::AccessoriesAndAttachments)
|
||||
{
|
||||
PartInstance* handle = getHandle();
|
||||
if (!handle)
|
||||
return;
|
||||
|
||||
// Code for new attachment based system
|
||||
Attachment* attachment = handle->findFirstChildOfType<Attachment>();
|
||||
Attachment* matchingAttachment = NULL;
|
||||
PartInstance* matchingAttachmentPart = NULL;
|
||||
if (attachment)
|
||||
{
|
||||
if ((matchingAttachment = Accoutrement::findFirstMatchingAttachment(getParent(), attachment->getName())))
|
||||
{
|
||||
matchingAttachmentPart = Instance::fastDynamicCast<PartInstance>(matchingAttachment->getParent());
|
||||
}
|
||||
}
|
||||
|
||||
// This infers that both attachments were found
|
||||
if (matchingAttachmentPart)
|
||||
{
|
||||
handle->setCanCollide(false);
|
||||
buildWeld(handle, matchingAttachmentPart, attachment->getFrameInPart(), matchingAttachment->getFrameInPart(), "AccessoryWeld");
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is legacy functionality
|
||||
if (PartInstance* head = humanoid->getHeadSlow())
|
||||
{
|
||||
handle->setCanCollide(false);
|
||||
buildWeld(head, handle, humanoid->getTopOfHead(), attachmentPoint, "HeadWeld");
|
||||
}
|
||||
}
|
||||
|
||||
// Listen to attachment changed to update weld
|
||||
connectAttachmentAdjustedEvent();
|
||||
}
|
||||
else
|
||||
{
|
||||
PartInstance* head = humanoid->getHeadSlow();
|
||||
if (!head)
|
||||
return;
|
||||
|
||||
PartInstance* handle = getHandle();
|
||||
if (handle)
|
||||
{
|
||||
handle->setCanCollide(false);
|
||||
buildWeld(head, handle, humanoid->getTopOfHead(), attachmentPoint, "HeadWeld");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Accoutrement::downFrom_Equipped()
|
||||
{
|
||||
ModelInstance* oldCharacter = NULL;
|
||||
|
||||
if (weld)
|
||||
{
|
||||
if (DFFlag::AccessoriesAndAttachments)
|
||||
{
|
||||
PartInstance* handle = getHandle();
|
||||
PartInstance* oldAttachmentPart = NULL;
|
||||
if (handle && (weld->getParent() == handle))
|
||||
{
|
||||
// If the weld is under the handle, then it is using the new attachment system
|
||||
oldAttachmentPart = weld->getPart1();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the weld is not undle the handle then it is using the legacy system
|
||||
oldAttachmentPart = weld->getPart0();
|
||||
}
|
||||
|
||||
if (oldAttachmentPart)
|
||||
{
|
||||
oldCharacter = Instance::fastDynamicCast<ModelInstance>(oldAttachmentPart->getParent());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PartInstance* oldArm = weld->getPart0())
|
||||
{
|
||||
oldCharacter = Instance::fastDynamicCast<ModelInstance>(oldArm->getParent());
|
||||
}
|
||||
}
|
||||
weld->setParent(NULL);
|
||||
weld.reset();
|
||||
}
|
||||
|
||||
if (oldCharacter && Humanoid::modelIsCharacter(oldCharacter))
|
||||
{
|
||||
CoordinateFrame cf = oldCharacter->getLocation();
|
||||
Vector3 pos = cf.translation;
|
||||
pos.y += 4;
|
||||
pos += cf.lookVector() * 8;
|
||||
|
||||
if (PartInstance* handle = getHandle())
|
||||
{
|
||||
handle->setCanCollide(true);
|
||||
handle->moveToPointNoJoin(pos);
|
||||
handle->setRotationalVelocity(Vector3(1.0f, 1.0f, 1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
// UGLY hack for when the camera is zooming in on the character, and he drops his hat, so the hat is not transparent.
|
||||
this->onCameraNear(999.0f);
|
||||
|
||||
// Dropping - must start listening again
|
||||
connectTouchEvent();
|
||||
|
||||
if (DFFlag::AccessoriesAndAttachments)
|
||||
{
|
||||
// Dropping - Doesn't need to be dynamically adjust weld
|
||||
attachmentAdjusted.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Backend stuff - events
|
||||
|
||||
|
||||
// Used for both the tool and the torso
|
||||
//
|
||||
// Character: RightArm, Tool
|
||||
// Torso: Shoulder?
|
||||
//
|
||||
void Accoutrement::onEvent_AddedBackend(shared_ptr<Instance> child)
|
||||
{
|
||||
if (child.get() != this) // tool parent changed is handled in ancestor stuff, because this might not be hooked up
|
||||
{
|
||||
AYAASSERT(ServiceProvider::findServiceProvider(this));
|
||||
AYAASSERT(Network::Players::backendProcessing(this));
|
||||
rebuildBackendState();
|
||||
}
|
||||
}
|
||||
|
||||
void Accoutrement::onEvent_RemovedBackend(shared_ptr<Instance> child)
|
||||
{
|
||||
if (child.get() != this) // tool parent changed is handled in ancestor stuff, because this might not be hooked up
|
||||
{
|
||||
AYAASSERT(ServiceProvider::findServiceProvider(this));
|
||||
AYAASSERT(Network::Players::backendProcessing(this));
|
||||
|
||||
if (child.get() == weld.get())
|
||||
{
|
||||
AYAASSERT(backendAccoutrementState >= EQUIPPED);
|
||||
setDesiredState(NOTHING, ServiceProvider::findServiceProvider(this)); // force down, nuke the weld
|
||||
}
|
||||
rebuildBackendState();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Accoutrement::onChildAdded(Instance* child)
|
||||
{
|
||||
if (ServiceProvider::findServiceProvider(this) && Network::Players::backendProcessing(this))
|
||||
{
|
||||
rebuildBackendState();
|
||||
}
|
||||
}
|
||||
|
||||
void Accoutrement::onChildRemoved(Instance* child)
|
||||
{
|
||||
if (ServiceProvider::findServiceProvider(this) && Network::Players::backendProcessing(this))
|
||||
{
|
||||
rebuildBackendState();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This is a backend event
|
||||
void Accoutrement::onEvent_HandleTouched(shared_ptr<Instance> other)
|
||||
{
|
||||
AYAASSERT(Network::Players::backendProcessing(this));
|
||||
|
||||
if ((backendAccoutrementState == IN_WORKSPACE) && other)
|
||||
{
|
||||
Instance* touchingCharacter = other->getParent();
|
||||
if (computeDesiredState(touchingCharacter) == EQUIPPED)
|
||||
{
|
||||
if (touchingCharacter->findFirstChildOfType<Accoutrement>() == NULL)
|
||||
{
|
||||
setParent(touchingCharacter);
|
||||
}
|
||||
|
||||
// AYAASSERT(backendAccoutrementState == EQUIPPED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Accoutrement::onEvent_AttachmentAdjusted(const Aya::Reflection::PropertyDescriptor* propertyDescriptor)
|
||||
{
|
||||
AYAASSERT(Network::Players::backendProcessing(this));
|
||||
|
||||
if (propertyDescriptor == &Aya::Attachment::prop_Frame)
|
||||
{
|
||||
updateWeld();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Accoutrement::onAncestorChanged(const AncestorChanged& event)
|
||||
{
|
||||
const ServiceProvider* oldProvider = ServiceProvider::findServiceProvider(event.oldParent);
|
||||
const ServiceProvider* newProvider = ServiceProvider::findServiceProvider(event.newParent);
|
||||
|
||||
if (oldProvider)
|
||||
{
|
||||
if (Network::Players::backendProcessing(oldProvider))
|
||||
{
|
||||
setDesiredState(NOTHING, oldProvider);
|
||||
}
|
||||
else
|
||||
{
|
||||
setBackendAccoutrementStateNoReplicate(NOTHING);
|
||||
}
|
||||
}
|
||||
|
||||
Super::onAncestorChanged(event);
|
||||
|
||||
if (newProvider)
|
||||
{
|
||||
if (Network::Players::backendProcessing(newProvider))
|
||||
{
|
||||
rebuildBackendState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Accoutrement::updateWeld()
|
||||
{
|
||||
if (weld != NULL)
|
||||
{
|
||||
// all this does not occur client side
|
||||
AYAASSERT(Network::Players::backendProcessing(this));
|
||||
|
||||
Attachment* attachment = NULL;
|
||||
|
||||
PartInstance* handle = getHandle();
|
||||
if (handle && (weld->getParent() == handle))
|
||||
{
|
||||
// If the weld is under the handle, then it is using the new attachment system
|
||||
attachment = handle->findFirstChildOfType<Attachment>();
|
||||
}
|
||||
|
||||
if (attachment)
|
||||
{
|
||||
// new attachment system
|
||||
weld->setC0(attachment->getFrameInPart());
|
||||
}
|
||||
else
|
||||
{
|
||||
// legacy hat
|
||||
weld->setC1(attachmentPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Accoutrement::setAttachmentPoint(const CoordinateFrame& value)
|
||||
{
|
||||
if (value != attachmentPoint)
|
||||
{
|
||||
attachmentPoint = value;
|
||||
|
||||
raisePropertyChanged(prop_AttachmentPoint);
|
||||
|
||||
if (DFFlag::AccessoriesAndAttachments)
|
||||
{
|
||||
updateWeld();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (weld != NULL)
|
||||
{
|
||||
// all this does not occur client side
|
||||
AYAASSERT(Network::Players::backendProcessing(this));
|
||||
weld->setC1(attachmentPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Auxillary UI props
|
||||
const Vector3 Accoutrement::getAttachmentPos() const
|
||||
{
|
||||
return attachmentPoint.translation;
|
||||
}
|
||||
|
||||
const Vector3 Accoutrement::getAttachmentForward() const
|
||||
{
|
||||
return attachmentPoint.lookVector();
|
||||
}
|
||||
|
||||
const Vector3 Accoutrement::getAttachmentUp() const
|
||||
{
|
||||
return attachmentPoint.upVector();
|
||||
}
|
||||
|
||||
const Vector3 Accoutrement::getAttachmentRight() const
|
||||
{
|
||||
return attachmentPoint.rightVector();
|
||||
}
|
||||
|
||||
void Accoutrement::setAttachmentPos(const Vector3& v)
|
||||
{
|
||||
setAttachmentPoint(CoordinateFrame(attachmentPoint.rotation, v));
|
||||
}
|
||||
|
||||
void Accoutrement::setAttachmentForward(const Vector3& v)
|
||||
{
|
||||
CoordinateFrame c(attachmentPoint);
|
||||
|
||||
// Vector3 oldX = c.rightVector();
|
||||
Vector3 oldY = c.upVector();
|
||||
|
||||
Vector3 z = -Math::safeDirection(v);
|
||||
|
||||
Vector3 x = oldY.cross(z);
|
||||
x.unitize();
|
||||
|
||||
Vector3 y = z.cross(x);
|
||||
y.unitize();
|
||||
|
||||
c.rotation.setColumn(0, x);
|
||||
c.rotation.setColumn(1, y);
|
||||
c.rotation.setColumn(2, z);
|
||||
|
||||
setAttachmentPoint(c);
|
||||
}
|
||||
|
||||
void Accoutrement::setAttachmentUp(const Vector3& v)
|
||||
{
|
||||
CoordinateFrame c(attachmentPoint);
|
||||
|
||||
Vector3 oldX = c.rightVector();
|
||||
// Vector3 oldZ = -c.lookVector();
|
||||
|
||||
Vector3 y = Math::safeDirection(v);
|
||||
|
||||
Vector3 z = oldX.cross(y);
|
||||
z.unitize();
|
||||
|
||||
Vector3 x = y.cross(z);
|
||||
x.unitize();
|
||||
|
||||
c.rotation.setColumn(0, x);
|
||||
c.rotation.setColumn(1, y);
|
||||
c.rotation.setColumn(2, z);
|
||||
|
||||
setAttachmentPoint(c);
|
||||
}
|
||||
|
||||
void Accoutrement::setAttachmentRight(const Vector3& v)
|
||||
{
|
||||
CoordinateFrame c(attachmentPoint);
|
||||
|
||||
Vector3 oldY = c.upVector();
|
||||
// Vector3 oldZ = -c.lookVector();
|
||||
|
||||
Vector3 x = Math::safeDirection(v);
|
||||
|
||||
Vector3 z = x.cross(oldY);
|
||||
z.unitize();
|
||||
|
||||
Vector3 y = z.cross(x);
|
||||
y.unitize();
|
||||
|
||||
c.rotation.setColumn(0, x);
|
||||
c.rotation.setColumn(1, y);
|
||||
c.rotation.setColumn(2, z);
|
||||
|
||||
setAttachmentPoint(c);
|
||||
}
|
||||
|
||||
Hat::Hat()
|
||||
: DescribedCreatable<Hat, Accoutrement, sHat>()
|
||||
{
|
||||
setName(sHat);
|
||||
}
|
||||
|
||||
Accessory::Accessory()
|
||||
: DescribedCreatable<Accessory, Accoutrement, sAccessory>()
|
||||
{
|
||||
setName(sAccessory);
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
198
engine/app/src/DataModel/Accoutrement.hpp
Normal file
198
engine/app/src/DataModel/Accoutrement.hpp
Normal file
@@ -0,0 +1,198 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DataModel/IEquipable.hpp"
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
#include "Tree/Instance.hpp"
|
||||
#include "Base/IAdornable.hpp"
|
||||
#include "Utility/CameraSubject.hpp"
|
||||
#include "Utility/Selectable.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class PartInstance;
|
||||
class ModelInstance;
|
||||
class Weld;
|
||||
class ServiceProvider;
|
||||
class Workspace;
|
||||
class Attachment;
|
||||
|
||||
namespace Network
|
||||
{
|
||||
class Player;
|
||||
}
|
||||
|
||||
extern const char* const sAccoutrement;
|
||||
extern const char* const sHat;
|
||||
extern const char* const sAccessory;
|
||||
|
||||
class Accoutrement
|
||||
: public DescribedCreatable<Accoutrement, Instance, sAccoutrement>
|
||||
, public IEquipable // TODO - move stuff here that's common with Tool
|
||||
, public IAdornable
|
||||
, public CameraSubject
|
||||
{
|
||||
typedef DescribedCreatable<Accoutrement, Instance, sAccoutrement> Super;
|
||||
|
||||
protected:
|
||||
typedef enum
|
||||
{
|
||||
NOTHING,
|
||||
HAS_HANDLE,
|
||||
IN_WORKSPACE,
|
||||
IN_CHARACTER,
|
||||
EQUIPPED
|
||||
} AccoutrementState;
|
||||
|
||||
// Replicated Properties
|
||||
AccoutrementState backendAccoutrementState; // backend writes, frontend reads
|
||||
CoordinateFrame attachmentPoint; // replicates, stores
|
||||
|
||||
// "Backend" connections
|
||||
Aya::signals::scoped_connection_logged handleTouched; // watches for handle touched
|
||||
Aya::signals::scoped_connection characterChildAdded;
|
||||
Aya::signals::scoped_connection characterChildRemoved;
|
||||
Aya::signals::scoped_connection attachmentAdjusted;
|
||||
|
||||
// Event handlers - hooked to these signals
|
||||
void onEvent_AddedBackend(shared_ptr<Instance> child);
|
||||
void onEvent_RemovedBackend(shared_ptr<Instance> child);
|
||||
void onEvent_HandleTouched(shared_ptr<Instance> other);
|
||||
void onEvent_AttachmentAdjusted(const Aya::Reflection::PropertyDescriptor*);
|
||||
|
||||
static AccoutrementState characterCanPickUpAccoutrement(Instance* touchingCharacter);
|
||||
|
||||
static void UnequipThis(shared_ptr<Instance> instance);
|
||||
|
||||
void updateWeld();
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Backend side
|
||||
//
|
||||
AccoutrementState computeDesiredState();
|
||||
AccoutrementState computeDesiredState(Instance* testParent);
|
||||
|
||||
|
||||
void setDesiredState(AccoutrementState desiredState, const ServiceProvider* serviceProvider);
|
||||
|
||||
void setBackendAccoutrementStateNoReplicate(int value);
|
||||
|
||||
void rebuildBackendState();
|
||||
|
||||
void connectTouchEvent();
|
||||
|
||||
void connectAttachmentAdjustedEvent();
|
||||
|
||||
// Backend - climbing state
|
||||
void upTo_Equipped();
|
||||
void upTo_InCharacter();
|
||||
void upTo_InWorkspace();
|
||||
void upTo_HasHandle();
|
||||
|
||||
|
||||
// Backend - dropping state
|
||||
void downFrom_Equipped();
|
||||
void downFrom_InCharacter();
|
||||
void downFrom_InWorkspace();
|
||||
void downFrom_HasHandle();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Frontend side
|
||||
|
||||
// Instance
|
||||
/*override*/ void onChildAdded(Instance* child);
|
||||
/*override*/ void onChildRemoved(Instance* child);
|
||||
/*override*/ void onAncestorChanged(const AncestorChanged& event);
|
||||
/*override*/ bool askSetParent(const Instance* instance) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
/*override*/ bool askAddChild(const Instance* instance) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// IHasLocation
|
||||
/*override*/ const CoordinateFrame getLocation();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// CameraSubject
|
||||
//
|
||||
/*override*/ const CoordinateFrame getRenderLocation()
|
||||
{
|
||||
return getLocation();
|
||||
}
|
||||
/*override*/ const Vector3 getRenderSize()
|
||||
{
|
||||
if (PartInstance* handle = getHandle())
|
||||
return handle->getPartSizeUi();
|
||||
return Vector3(0, 0, 0);
|
||||
}
|
||||
/*override*/ void onCameraNear(float distance);
|
||||
|
||||
// BackpackItem
|
||||
/*override*/ bool drawSelected() const
|
||||
{
|
||||
return (backendAccoutrementState >= EQUIPPED);
|
||||
}
|
||||
|
||||
// IAdornable
|
||||
/*override*/ void render3dSelect(Adorn* adorn, SelectState selectState);
|
||||
|
||||
public:
|
||||
Accoutrement();
|
||||
~Accoutrement();
|
||||
|
||||
static void dropAll(ModelInstance* character);
|
||||
static void dropAllOthers(ModelInstance* character, Accoutrement* exception);
|
||||
|
||||
|
||||
PartInstance* getHandle();
|
||||
const PartInstance* getHandleConst() const;
|
||||
|
||||
Attachment* findFirstMatchingAttachment(Instance* model, const std::string& originalAttachment);
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
//
|
||||
// REPLICATION Signals
|
||||
void setBackendAccoutrementState(int value);
|
||||
int getBackendAccoutrementState() const
|
||||
{
|
||||
return backendAccoutrementState;
|
||||
}
|
||||
|
||||
const CoordinateFrame& getAttachmentPoint() const
|
||||
{
|
||||
return attachmentPoint;
|
||||
}
|
||||
void setAttachmentPoint(const CoordinateFrame& value);
|
||||
|
||||
// Auxillary UI props
|
||||
const Vector3 getAttachmentPos() const;
|
||||
const Vector3 getAttachmentForward() const;
|
||||
const Vector3 getAttachmentUp() const;
|
||||
const Vector3 getAttachmentRight() const;
|
||||
|
||||
void setAttachmentPos(const Vector3& v);
|
||||
void setAttachmentForward(const Vector3& v);
|
||||
void setAttachmentUp(const Vector3& v);
|
||||
void setAttachmentRight(const Vector3& v);
|
||||
};
|
||||
|
||||
|
||||
class Hat : public DescribedCreatable<Hat, Accoutrement, sHat>
|
||||
{
|
||||
public:
|
||||
Hat();
|
||||
};
|
||||
|
||||
class Accessory : public DescribedCreatable<Accessory, Accoutrement, sAccessory>
|
||||
{
|
||||
public:
|
||||
Accessory();
|
||||
};
|
||||
} // namespace Aya
|
||||
60
engine/app/src/DataModel/ActionStation.hpp
Normal file
60
engine/app/src/DataModel/ActionStation.hpp
Normal file
@@ -0,0 +1,60 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DataModel/JointInstance.hpp"
|
||||
#include "DataModel/Workspace.hpp"
|
||||
#include "Humanoid/Humanoid.hpp"
|
||||
#include "World/Primitive.hpp"
|
||||
#include "World/Assembly.hpp"
|
||||
#include "time.hpp"
|
||||
|
||||
DYNAMIC_FASTINT(ActionStationDebounceTime)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
|
||||
template<class Base>
|
||||
class ActionStation : public Base
|
||||
{
|
||||
private:
|
||||
typedef Base Super;
|
||||
|
||||
protected:
|
||||
Time sleepTime;
|
||||
Time debounceTime;
|
||||
|
||||
bool sleepTimeUp() const
|
||||
{
|
||||
return (Time::now<Time::Fast>() - sleepTime).seconds() > 3.0;
|
||||
}
|
||||
|
||||
bool debounceTimeUp() const
|
||||
{
|
||||
return (Time::now<Time::Fast>() - debounceTime).seconds() > DFInt::ActionStationDebounceTime;
|
||||
}
|
||||
|
||||
// Instance
|
||||
// TODO: Ultra mega super hack - setName is setting the internal Primitive::sizeMultiplier
|
||||
void setName(const std::string& value)
|
||||
{
|
||||
Super::setName(value);
|
||||
this->getPartPrimitive()->setSizeMultiplier(Primitive::SEAT_SIZE);
|
||||
}
|
||||
|
||||
public:
|
||||
ActionStation()
|
||||
: sleepTime(Time::now<Time::Fast>() - Time::Interval(4.0))
|
||||
, debounceTime(Time::now<Time::Fast>())
|
||||
{
|
||||
AYAASSERT(this->sleepTimeUp());
|
||||
AYAASSERT(this->getPartPrimitive());
|
||||
this->getPartPrimitive()->setSizeMultiplier(Primitive::SEAT_SIZE);
|
||||
}
|
||||
|
||||
virtual ~ActionStation() {}
|
||||
};
|
||||
|
||||
|
||||
} // namespace Aya
|
||||
257
engine/app/src/DataModel/AdService.cpp
Normal file
257
engine/app/src/DataModel/AdService.cpp
Normal file
@@ -0,0 +1,257 @@
|
||||
//
|
||||
// AdService.cpp
|
||||
// App
|
||||
//
|
||||
// Created by Ben Tkacheff on 4/16/14.
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
#include "DataModel/AdService.hpp"
|
||||
#include "DataModel/UserInputService.hpp"
|
||||
#include "DataModel/HttpRbxApiService.hpp"
|
||||
#include "Players.hpp"
|
||||
#include "Xml/WebParser.hpp"
|
||||
|
||||
FASTFLAGVARIABLE(EnableVideoAds, true)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
const char* const sAdService = "AdService";
|
||||
|
||||
|
||||
static Reflection::RemoteEventDesc<AdService, void(int, UserInputService::Platform, bool)> event_sendServerRecordImpression(
|
||||
&AdService::sendServerRecordImpression, "SendServerRecordImpression", "userId", "platform", "wasSuccessful", Security::Roblox,
|
||||
Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::CLIENT_SERVER);
|
||||
|
||||
static Reflection::RemoteEventDesc<AdService, void(bool, int, std::string)> event_sendClientAdVerificationResults(
|
||||
&AdService::sendClientVideoAdVerificationResults, "ClientAdVerificationResults", "canShowAd", "userId", "errorMessage", Security::Roblox,
|
||||
Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
|
||||
static Reflection::RemoteEventDesc<AdService, void(int, UserInputService::Platform)> event_serverAdVerification(
|
||||
&AdService::sendServerVideoAdVerification, "ServerAdVerification", "userId", "platform", Security::Roblox,
|
||||
Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::CLIENT_SERVER);
|
||||
|
||||
static Reflection::BoundFuncDesc<AdService, void()> func_showVideoAd(&AdService::showVideoAd, "ShowVideoAd", Security::None);
|
||||
static Reflection::EventDesc<AdService, void(bool)> event_videoClosed(&AdService::videoAdClosedSignal, "VideoAdClosed", "adShown");
|
||||
REFLECTION_END();
|
||||
|
||||
AdService::AdService()
|
||||
: showingVideoAd(false)
|
||||
{
|
||||
setName(sAdService);
|
||||
}
|
||||
|
||||
bool AdService::canUseService()
|
||||
{
|
||||
return FFlag::EnableVideoAds;
|
||||
}
|
||||
|
||||
void AdService::showVideoAd()
|
||||
{
|
||||
if (!canUseService())
|
||||
{
|
||||
StandardOut::singleton()->printf(MESSAGE_WARNING, "Sorry, AdService is currently off.");
|
||||
videoAdClosedSignal(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (showingVideoAd)
|
||||
{
|
||||
StandardOut::singleton()->printf(MESSAGE_WARNING, "AdService:ShowVideoAd called while currently showing video.");
|
||||
videoAdClosedSignal(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Aya::UserInputService* inputService = Aya::ServiceProvider::find<Aya::UserInputService>(this))
|
||||
{
|
||||
if (!inputService->getTouchEnabled())
|
||||
{
|
||||
StandardOut::singleton()->printf(MESSAGE_WARNING, "Sorry, AdService:ShowVideoAd only works on touch devices currently.");
|
||||
videoAdClosedSignal(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Network::Players::frontendProcessing(this))
|
||||
{
|
||||
StandardOut::singleton()->printf(MESSAGE_WARNING, "AdService:ShowVideoAd must be called from a local script!");
|
||||
videoAdClosedSignal(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Network::Player* player = Network::Players::findLocalPlayer(this))
|
||||
{
|
||||
if (Aya::ServiceProvider::find<Aya::UserInputService>(this))
|
||||
{
|
||||
event_serverAdVerification.fireAndReplicateEvent(this, player->getUserID(), UserInputService::getPlatform());
|
||||
}
|
||||
else
|
||||
{
|
||||
StandardOut::singleton()->printf(MESSAGE_WARNING, "AdService:ShowVideoAd could not find UserInputService, not showing ad.");
|
||||
videoAdClosedSignal(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
StandardOut::singleton()->printf(MESSAGE_WARNING, "AdService:ShowVideoAd could not find LocalPlayer, not showing ad.");
|
||||
videoAdClosedSignal(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void AdService::receivedServerShowAdMessage(const bool success, int userId, const std::string& errorMessage)
|
||||
{
|
||||
if (!Aya::Network::Players::clientIsPresent(this))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Network::Player* player = Network::Players::findLocalPlayer(this);
|
||||
if (!player)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (player->getUserID() != userId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
showingVideoAd = true;
|
||||
playVideoAdSignal();
|
||||
}
|
||||
else
|
||||
{
|
||||
StandardOut::singleton()->printf(
|
||||
MESSAGE_WARNING, "AdService:ShowVideoAd cannot show video ad, failed verification because %s", errorMessage.c_str());
|
||||
videoAdClosedSignal(false);
|
||||
}
|
||||
}
|
||||
|
||||
void AdService::verifyCanPlayVideoAdReceivedResponseNoDMLock(const std::string& response, int userId)
|
||||
{
|
||||
shared_ptr<const Reflection::ValueTable> jsonResult(Aya::make_shared<const Reflection::ValueTable>());
|
||||
bool parseResult = WebParser::parseJSONTable(response, jsonResult);
|
||||
|
||||
if (parseResult)
|
||||
{
|
||||
Reflection::Variant successVar = jsonResult->at("success");
|
||||
|
||||
if (successVar.get<bool>())
|
||||
{
|
||||
event_sendClientAdVerificationResults.fireAndReplicateEvent(this, true, userId, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (jsonResult->at("errorMessage").isString())
|
||||
{
|
||||
verifyCanPlayVideoAdReceivedError(jsonResult->at("errorMessage").get<std::string>(), userId);
|
||||
}
|
||||
else
|
||||
{
|
||||
verifyCanPlayVideoAdReceivedError("JSON returned invalid format", userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
verifyCanPlayVideoAdReceivedError("Unable to parse JSON response", userId);
|
||||
}
|
||||
}
|
||||
|
||||
void AdService::verifyCanPlayVideoAdReceivedError(const std::string& error, int userId)
|
||||
{
|
||||
Aya::DataModel::LegacyLock dmLock(Aya::DataModel::get(this), Aya::DataModelJob::Write);
|
||||
event_sendClientAdVerificationResults.fireAndReplicateEvent(this, false, userId, error);
|
||||
}
|
||||
|
||||
void AdService::verifyCanPlayVideoAdReceivedErrorNoDMLock(const std::string& error, int userId)
|
||||
{
|
||||
event_sendClientAdVerificationResults.fireAndReplicateEvent(this, false, userId, error);
|
||||
}
|
||||
|
||||
std::string AdService::platformToWebString(const UserInputService::Platform userPlatform)
|
||||
{
|
||||
switch (userPlatform)
|
||||
{
|
||||
case UserInputService::PLATFORM_IOS:
|
||||
return "ios";
|
||||
case UserInputService::PLATFORM_ANDROID:
|
||||
return "android";
|
||||
default:
|
||||
return "unknown";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AdService::checkCanPlayVideoAd(int userId, UserInputService::Platform userPlatform)
|
||||
{
|
||||
if (!Aya::Network::Players::serverIsPresent(this))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Aya::DataModel* dataModel = Aya::DataModel::get(this))
|
||||
{
|
||||
std::string data =
|
||||
Aya::format("userId=%d&placeId=%d&deviceOSType=%s", userId, dataModel->getPlaceID(), platformToWebString(userPlatform).c_str());
|
||||
|
||||
if (Aya::HttpRbxApiService* apiService = Aya::ServiceProvider::find<Aya::HttpRbxApiService>(this))
|
||||
{
|
||||
apiService->postAsync(Aya::format("adimpression/validate-request?%s", data.c_str()), std::string("ServerCanPlayAd"), true,
|
||||
Aya::PRIORITY_DEFAULT, HttpService::TEXT_PLAIN,
|
||||
boost::bind(&AdService::verifyCanPlayVideoAdReceivedResponseNoDMLock, this, _1, userId),
|
||||
boost::bind(&AdService::verifyCanPlayVideoAdReceivedErrorNoDMLock, this, _1, userId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AdService::videoAdClosed(bool didPlay)
|
||||
{
|
||||
showingVideoAd = false;
|
||||
videoAdClosedSignal(didPlay);
|
||||
|
||||
if (Network::Player* player = Network::Players::findLocalPlayer(this))
|
||||
{
|
||||
if (Aya::ServiceProvider::find<Aya::UserInputService>(this))
|
||||
{
|
||||
event_sendServerRecordImpression.fireAndReplicateEvent(this, player->getUserID(), UserInputService::getPlatform(), didPlay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ForgetResponse(const std::string& response) {}
|
||||
|
||||
void AdService::sendAdImpression(int userId, UserInputService::Platform platform, bool didPlay)
|
||||
{
|
||||
if (Aya::DataModel* dataModel = Aya::DataModel::get(this))
|
||||
{
|
||||
if (Network::Players::serverIsPresent(dataModel))
|
||||
{
|
||||
std::string apiBaseUrl = ServiceProvider::create<ContentProvider>(this)->getApiBaseUrl();
|
||||
// fire and forget, we don't care about results on the client, at least for now
|
||||
std::string data = Aya::format("userId=%i&placeId=%d&deviceOSType=%s&wasSuccessful=%s", userId, dataModel->getPlaceID(),
|
||||
platformToWebString(platform).c_str(), didPlay ? "true" : "false");
|
||||
|
||||
if (Aya::HttpRbxApiService* apiService = Aya::ServiceProvider::find<Aya::HttpRbxApiService>(this))
|
||||
{
|
||||
apiService->postAsync(Aya::format("adimpression/record-impression?%s", data.c_str()), std::string("ServerSendAdImpression"), true,
|
||||
Aya::PRIORITY_DEFAULT, HttpService::TEXT_PLAIN, boost::bind(&ForgetResponse, _1), boost::bind(&ForgetResponse, _1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AdService::onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
|
||||
{
|
||||
Super::onServiceProvider(oldProvider, newProvider);
|
||||
|
||||
if (newProvider && Network::Players::frontendProcessing(newProvider))
|
||||
{
|
||||
sendClientVideoAdVerificationResults.connect(boost::bind(&AdService::receivedServerShowAdMessage, this, _1, _2, _3));
|
||||
}
|
||||
}
|
||||
} // namespace Aya
|
||||
54
engine/app/src/DataModel/AdService.hpp
Normal file
54
engine/app/src/DataModel/AdService.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// AdService.h
|
||||
//
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Tree/Instance.hpp"
|
||||
#include "Tree/Service.hpp"
|
||||
#include "DataModel/UserInputService.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
extern const char* const sAdService;
|
||||
class AdService
|
||||
: public DescribedCreatable<AdService, Instance, sAdService, Reflection::ClassDescriptor::INTERNAL>
|
||||
, public Service
|
||||
{
|
||||
private:
|
||||
typedef DescribedCreatable<AdService, Instance, sAdService, Reflection::ClassDescriptor::INTERNAL> Super;
|
||||
|
||||
bool showingVideoAd;
|
||||
|
||||
std::string platformToWebString(const UserInputService::Platform userPlatform);
|
||||
bool canUseService();
|
||||
|
||||
protected:
|
||||
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
|
||||
|
||||
public:
|
||||
AdService();
|
||||
|
||||
Aya::signal<void(bool)> videoAdClosedSignal;
|
||||
Aya::signal<void()> playVideoAdSignal;
|
||||
|
||||
Aya::remote_signal<void(int, UserInputService::Platform)> sendServerVideoAdVerification;
|
||||
Aya::remote_signal<void(bool, int, std::string)> sendClientVideoAdVerificationResults;
|
||||
|
||||
Aya::remote_signal<void(int, UserInputService::Platform, bool)> sendServerRecordImpression;
|
||||
|
||||
void showVideoAd();
|
||||
void videoAdClosed(bool didPlay);
|
||||
|
||||
void sendAdImpression(int userId, UserInputService::Platform platform, bool didPlay);
|
||||
|
||||
void verifyCanPlayVideoAdReceivedResponseNoDMLock(const std::string& response, int userId);
|
||||
void verifyCanPlayVideoAdReceivedErrorNoDMLock(const std::string& error, int userId);
|
||||
|
||||
void verifyCanPlayVideoAdReceivedError(const std::string& error, int userId);
|
||||
|
||||
void checkCanPlayVideoAd(int userId, UserInputService::Platform userPlatform);
|
||||
void receivedServerShowAdMessage(const bool success, int userId, const std::string& errorMessage);
|
||||
};
|
||||
} // namespace Aya
|
||||
49
engine/app/src/DataModel/Adornment.cpp
Normal file
49
engine/app/src/DataModel/Adornment.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
|
||||
|
||||
|
||||
#include "DataModel/Adornment.hpp"
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
#include "DataModel/PVInstance.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
const char* const sPartAdornment = "PartAdornment";
|
||||
|
||||
const Reflection::RefPropDescriptor<PartAdornment, PartInstance> prop_partAdornee(
|
||||
"Adornee", category_Data, &PartAdornment::getAdorneeDangerous, &PartAdornment::setAdornee);
|
||||
|
||||
PartAdornment::PartAdornment(const char* name)
|
||||
: DescribedNonCreatable<PartAdornment, GuiBase3d, sPartAdornment>(name)
|
||||
{
|
||||
}
|
||||
void PartAdornment::setAdornee(PartInstance* value)
|
||||
{
|
||||
if (adornee.lock().get() != value)
|
||||
{
|
||||
adornee = shared_from(value);
|
||||
raisePropertyChanged(prop_partAdornee);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const char* const sPVAdornment = "PVAdornment";
|
||||
|
||||
const Reflection::RefPropDescriptor<PVAdornment, PVInstance> prop_pvAdornee(
|
||||
"Adornee", category_Data, &PVAdornment::getAdorneeDangerous, &PVAdornment::setAdornee);
|
||||
|
||||
PVAdornment::PVAdornment(const char* name)
|
||||
: DescribedNonCreatable<PVAdornment, GuiBase3d, sPVAdornment>(name)
|
||||
{
|
||||
}
|
||||
void PVAdornment::setAdornee(PVInstance* value)
|
||||
{
|
||||
if (adornee.lock().get() != value)
|
||||
{
|
||||
adornee = shared_from(value);
|
||||
raisePropertyChanged(prop_pvAdornee);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
65
engine/app/src/DataModel/Adornment.hpp
Normal file
65
engine/app/src/DataModel/Adornment.hpp
Normal file
@@ -0,0 +1,65 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DataModel/GuiBase3d.hpp"
|
||||
#include "Utility/BrickColor.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
class PartInstance;
|
||||
class PVInstance;
|
||||
|
||||
extern const char* const sPartAdornment;
|
||||
|
||||
// A base class for "adornment" of PartInstances (3D objects that adorn Instances)
|
||||
class PartAdornment : public DescribedNonCreatable<PartAdornment, GuiBase3d, sPartAdornment>
|
||||
{
|
||||
public:
|
||||
PartAdornment(const char* name);
|
||||
|
||||
const PartInstance* getAdornee() const
|
||||
{
|
||||
return adornee.lock().get();
|
||||
}
|
||||
PartInstance* getAdornee()
|
||||
{
|
||||
return adornee.lock().get();
|
||||
}
|
||||
void setAdornee(PartInstance* value);
|
||||
PartInstance* getAdorneeDangerous() const
|
||||
{
|
||||
return adornee.lock().get();
|
||||
}
|
||||
|
||||
protected:
|
||||
weak_ptr<PartInstance> adornee;
|
||||
};
|
||||
|
||||
extern const char* const sPVAdornment;
|
||||
|
||||
// A base class for "adornment" of PartInstances (3D objects that adorn Instances)
|
||||
class PVAdornment : public DescribedNonCreatable<PVAdornment, GuiBase3d, sPVAdornment>
|
||||
{
|
||||
public:
|
||||
PVAdornment(const char* name);
|
||||
|
||||
const PVInstance* getAdornee() const
|
||||
{
|
||||
return adornee.lock().get();
|
||||
}
|
||||
PVInstance* getAdornee()
|
||||
{
|
||||
return adornee.lock().get();
|
||||
}
|
||||
void setAdornee(PVInstance* value);
|
||||
PVInstance* getAdorneeDangerous() const
|
||||
{
|
||||
return adornee.lock().get();
|
||||
}
|
||||
|
||||
protected:
|
||||
weak_ptr<PVInstance> adornee;
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
55
engine/app/src/DataModel/AnimatableRootJoint.cpp
Normal file
55
engine/app/src/DataModel/AnimatableRootJoint.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
|
||||
|
||||
#include "DataModel/AnimatableRootJoint.hpp"
|
||||
|
||||
using namespace Aya;
|
||||
|
||||
AnimatableRootJoint::AnimatableRootJoint(const shared_ptr<PartInstance>& part)
|
||||
: IAnimatableJoint()
|
||||
, part(part)
|
||||
, isAnimating(false)
|
||||
{
|
||||
}
|
||||
|
||||
void AnimatableRootJoint::setAnimating(bool value)
|
||||
{
|
||||
if (isAnimating != value && value)
|
||||
{
|
||||
// reset.
|
||||
lastCFrame = CoordinateFrame();
|
||||
}
|
||||
isAnimating = value;
|
||||
}
|
||||
|
||||
const std::string& AnimatableRootJoint::getParentName()
|
||||
{
|
||||
return IAnimatableJoint::sROOT;
|
||||
}
|
||||
|
||||
const std::string& AnimatableRootJoint::getPartName()
|
||||
{
|
||||
return part->getName();
|
||||
}
|
||||
|
||||
void AnimatableRootJoint::applyPose(const CachedPose& pose)
|
||||
{
|
||||
// nb: we completely ignore blend weights unless they are zero
|
||||
// nb: we ignore maskWeight and pretend it is 1. (since we don't prevent physics from acting)
|
||||
CoordinateFrame poseCFrame = pose.getCFrame();
|
||||
if (pose.weight > 0)
|
||||
{
|
||||
// to let physics act on the part, we extract the change from last frame.
|
||||
// then just apply the change.
|
||||
// toObjectSpace is this->inverse() * c;
|
||||
// delta = last^-1 * pose
|
||||
// newcframe = currentcframe * delta
|
||||
// newcframe = currentcframe * last^-1 * pose
|
||||
// so if currentcframe == last (eg: part was anchored), then
|
||||
// newcframe = pose
|
||||
CoordinateFrame delta = lastCFrame.toObjectSpace(poseCFrame);
|
||||
|
||||
part->setCoordinateFrame(part->getCoordinateFrame() * delta);
|
||||
}
|
||||
|
||||
lastCFrame = poseCFrame;
|
||||
}
|
||||
26
engine/app/src/DataModel/AnimatableRootJoint.hpp
Normal file
26
engine/app/src/DataModel/AnimatableRootJoint.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
#include "DataModel/IAnimatableJoint.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
class AnimatableRootJoint : public IAnimatableJoint
|
||||
{
|
||||
bool isAnimating;
|
||||
shared_ptr<PartInstance> part;
|
||||
CoordinateFrame lastCFrame;
|
||||
|
||||
public:
|
||||
AnimatableRootJoint(const shared_ptr<PartInstance>& part);
|
||||
PartInstance* getPart() const
|
||||
{
|
||||
return part.get();
|
||||
}
|
||||
|
||||
/*override*/ void setAnimating(bool value);
|
||||
/*override*/ const std::string& getParentName();
|
||||
/*override*/ const std::string& getPartName();
|
||||
/*override*/ void applyPose(const CachedPose& pose);
|
||||
};
|
||||
} // namespace Aya
|
||||
65
engine/app/src/DataModel/Animation.cpp
Normal file
65
engine/app/src/DataModel/Animation.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
|
||||
|
||||
|
||||
#include "DataModel/Animation.hpp"
|
||||
#include "DataModel/KeyframeSequenceProvider.hpp"
|
||||
#include "DataModel/AnimationTrackState.hpp"
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
#include "DataModel/JointInstance.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
const char* const sAnimation = "Animation";
|
||||
|
||||
const Reflection::PropDescriptor<Animation, AnimationId> prop_AnimationId(
|
||||
"AnimationId", category_Data, &Animation::getAssetId, &Animation::setAssetId);
|
||||
// const Reflection::PropDescriptor<Animation, bool> prop_Loop("Loop", category_Data, &Animation::getLoop, &Animation::setLoop);
|
||||
// const Reflection::EnumPropDescriptor<Animation, Animation::Priority> prop_Priority("Priority", category_Data, &Animation::getPriority,
|
||||
// &Animation::setPriority);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
// FRONTEND AND BACKEND
|
||||
|
||||
Animation::Animation()
|
||||
: DescribedCreatable<Animation, Instance, sAnimation>()
|
||||
{
|
||||
setName(sAnimation);
|
||||
}
|
||||
|
||||
bool Animation::isEmbeddedAsset() const
|
||||
{
|
||||
return !assetId.isHttp() && !assetId.isAsset();
|
||||
}
|
||||
|
||||
shared_ptr<const KeyframeSequence> Animation::getKeyframeSequence() const
|
||||
{
|
||||
return getKeyframeSequence(this);
|
||||
}
|
||||
|
||||
shared_ptr<const KeyframeSequence> Animation::getKeyframeSequence(const Instance* context) const
|
||||
{
|
||||
if (assetId.isNull())
|
||||
{
|
||||
return shared_ptr<KeyframeSequence>();
|
||||
}
|
||||
|
||||
if (KeyframeSequenceProvider* keyframeSequenceProvider = ServiceProvider::create<KeyframeSequenceProvider>(context))
|
||||
{
|
||||
return keyframeSequenceProvider->getKeyframeSequence(assetId, this);
|
||||
}
|
||||
return shared_ptr<KeyframeSequence>();
|
||||
}
|
||||
|
||||
void Animation::setAssetId(AnimationId value)
|
||||
{
|
||||
if (assetId != value)
|
||||
{
|
||||
assetId = value;
|
||||
raisePropertyChanged(prop_AnimationId);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
43
engine/app/src/DataModel/Animation.hpp
Normal file
43
engine/app/src/DataModel/Animation.hpp
Normal file
@@ -0,0 +1,43 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Tree/Instance.hpp"
|
||||
#include "Utility/AnimationId.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
extern const char* const sAnimation;
|
||||
|
||||
class KeyframeSequence;
|
||||
class Animation : public DescribedCreatable<Animation, Instance, sAnimation>
|
||||
{
|
||||
private:
|
||||
typedef DescribedCreatable<Animation, Instance, sAnimation> Super;
|
||||
ContentId assetId;
|
||||
|
||||
public:
|
||||
Animation();
|
||||
|
||||
shared_ptr<const KeyframeSequence> getKeyframeSequence() const;
|
||||
|
||||
shared_ptr<const KeyframeSequence> getKeyframeSequence(const Instance* context) const;
|
||||
|
||||
AnimationId getAssetId() const
|
||||
{
|
||||
return assetId;
|
||||
}
|
||||
void setAssetId(AnimationId value);
|
||||
|
||||
bool isEmbeddedAsset() const;
|
||||
|
||||
/*override*/ bool askSetParent(const Instance* instance) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
/*override*/ int getPersistentDataCost() const
|
||||
{
|
||||
return Super::getPersistentDataCost() + Instance::computeStringCost(getAssetId().toString());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
79
engine/app/src/DataModel/AnimationController.cpp
Normal file
79
engine/app/src/DataModel/AnimationController.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
|
||||
|
||||
|
||||
#include "DataModel/AnimationController.hpp"
|
||||
#include "DataModel/Workspace.hpp"
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
#include "DataModel/Animator.hpp"
|
||||
#include "World/World.hpp"
|
||||
#include "Tree/Service.hpp"
|
||||
#include "Kernel/Constants.hpp"
|
||||
#include "Players.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
const char* const sAnimationController = "AnimationController";
|
||||
|
||||
|
||||
static Reflection::BoundFuncDesc<AnimationController, shared_ptr<const Reflection::ValueArray>()> desc_GetPlayingAnimationTracks(
|
||||
&AnimationController::getPlayingAnimationTracks, "GetPlayingAnimationTracks", Security::None);
|
||||
static Reflection::BoundFuncDesc<AnimationController, shared_ptr<Instance>(shared_ptr<Instance>)> desc_LoadAnimation(
|
||||
&AnimationController::loadAnimation, "LoadAnimation", "animation", Security::None);
|
||||
static Reflection::EventDesc<AnimationController, void(shared_ptr<Instance>)> event_AnimationPlayed(
|
||||
&AnimationController::animationPlayedSignal, "AnimationPlayed", "animationTrack");
|
||||
REFLECTION_END();
|
||||
|
||||
AnimationController::AnimationController()
|
||||
{
|
||||
setName("AnimationController");
|
||||
FASTLOG1(FLog::ISteppedLifetime, "AnimationController created - %p", this);
|
||||
}
|
||||
|
||||
AnimationController::~AnimationController()
|
||||
{
|
||||
FASTLOG1(FLog::ISteppedLifetime, "AnimationController destroyed - %p", this);
|
||||
}
|
||||
|
||||
|
||||
bool AnimationController::askSetParent(const Instance* instance) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void AnimationController::onStepped(const Stepped& event)
|
||||
{
|
||||
if (animator)
|
||||
{
|
||||
animator->onStepped(event);
|
||||
}
|
||||
}
|
||||
|
||||
Animator* AnimationController::getAnimator()
|
||||
{
|
||||
if (!animator)
|
||||
{
|
||||
animator = shared_ptr<Animator>(Creatable<Instance>::create<Aya::Animator>(this));
|
||||
if (getParent() && getParent()->getClassNameStr() == "Model")
|
||||
animator->setParent(this);
|
||||
}
|
||||
return animator.get();
|
||||
}
|
||||
|
||||
shared_ptr<const Reflection::ValueArray> AnimationController::getPlayingAnimationTracks()
|
||||
{
|
||||
return getAnimator()->getPlayingAnimationTracks();
|
||||
}
|
||||
|
||||
shared_ptr<Instance> AnimationController::loadAnimation(shared_ptr<Instance> instance)
|
||||
{
|
||||
return getAnimator()->loadAnimation(instance);
|
||||
}
|
||||
|
||||
void AnimationController::onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
|
||||
{
|
||||
Super::onServiceProvider(oldProvider, newProvider);
|
||||
onServiceProviderIStepped(oldProvider, newProvider);
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
44
engine/app/src/DataModel/AnimationController.hpp
Normal file
44
engine/app/src/DataModel/AnimationController.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Utility/SteppedInstance.hpp"
|
||||
#include "Utility/RunStateOwner.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
// class Workspace;
|
||||
// class Primitive;
|
||||
// class PartInstance;
|
||||
class Animator;
|
||||
|
||||
extern const char* const sAnimationController;
|
||||
class AnimationController
|
||||
: public DescribedCreatable<AnimationController, Instance, sAnimationController>
|
||||
, public IStepped
|
||||
{
|
||||
private:
|
||||
typedef DescribedCreatable<AnimationController, Instance, sAnimationController> Super;
|
||||
|
||||
shared_ptr<Animator> animator;
|
||||
Animator* getAnimator();
|
||||
|
||||
// Instance
|
||||
/*override*/ bool askSetParent(const Instance* instance) const;
|
||||
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
|
||||
|
||||
// IStepped
|
||||
/*override*/ void onStepped(const Stepped& event);
|
||||
|
||||
public:
|
||||
AnimationController();
|
||||
virtual ~AnimationController();
|
||||
|
||||
shared_ptr<Instance> loadAnimation(shared_ptr<Instance> animation);
|
||||
shared_ptr<const Reflection::ValueArray> getPlayingAnimationTracks();
|
||||
|
||||
Aya::signal<void(shared_ptr<Instance>)> animationPlayedSignal;
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
249
engine/app/src/DataModel/AnimationTrack.cpp
Normal file
249
engine/app/src/DataModel/AnimationTrack.cpp
Normal file
@@ -0,0 +1,249 @@
|
||||
|
||||
|
||||
|
||||
#include "DataModel/AnimationTrack.hpp"
|
||||
#include "DataModel/Animator.hpp"
|
||||
#include "DataModel/AnimationTrackState.hpp"
|
||||
#include "DataModel/Animation.hpp"
|
||||
#include "DataModel/Workspace.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
namespace Reflection
|
||||
{
|
||||
template<>
|
||||
const Type& Type::getSingleton<shared_ptr<AnimationTrack>>()
|
||||
{
|
||||
static TType<shared_ptr<AnimationTrack>> type("AnimationTrack");
|
||||
return type;
|
||||
}
|
||||
} // namespace Reflection
|
||||
|
||||
const char* const sAnimationTrack = "AnimationTrack";
|
||||
|
||||
|
||||
static Reflection::BoundFuncDesc<AnimationTrack, void(float, float, float)> desc_Play(
|
||||
&AnimationTrack::play, "Play", "fadeTime", 0.1f, "weight", 1.0f, "speed", 1.0f, Security::None);
|
||||
static Reflection::BoundFuncDesc<AnimationTrack, void(float)> desc_Stop(&AnimationTrack::stop, "Stop", "fadeTime", 0.1f, Security::None);
|
||||
static Reflection::BoundFuncDesc<AnimationTrack, void(float, float)> desc_AdjustWeight(
|
||||
&AnimationTrack::adjustWeight, "AdjustWeight", "weight", 1.0f, "fadeTime", 0.1f, Security::None);
|
||||
static Reflection::BoundFuncDesc<AnimationTrack, void(float)> desc_AdjustSpeed(
|
||||
&AnimationTrack::adjustSpeed, "AdjustSpeed", "speed", 1.0f, Security::None);
|
||||
static Reflection::BoundFuncDesc<AnimationTrack, double(std::string)> desc_GetTimeOfKeyframe(
|
||||
&AnimationTrack::getTimeOfKeyframe, "GetTimeOfKeyframe", "keyframeName", Security::None);
|
||||
|
||||
static Reflection::PropDescriptor<AnimationTrack, float> prop_Length(
|
||||
"Length", category_Data, &AnimationTrack::getLength, NULL, Reflection::PropertyDescriptor::SCRIPTING);
|
||||
static Reflection::PropDescriptor<AnimationTrack, bool> prop_IsPlaying(
|
||||
"IsPlaying", category_Data, &AnimationTrack::getIsPlaying, NULL, Reflection::PropertyDescriptor::SCRIPTING);
|
||||
static Reflection::PropDescriptor<AnimationTrack, float> prop_TimePosition(
|
||||
"TimePosition", category_Data, &AnimationTrack::getTimePosition, &AnimationTrack::setTimePosition, Reflection::PropertyDescriptor::UI);
|
||||
|
||||
static Reflection::RefPropDescriptor<AnimationTrack, Animation> prop_Animation(
|
||||
"Animation", category_Data, &AnimationTrack::getAnimation, NULL, Reflection::PropertyDescriptor::SCRIPTING);
|
||||
|
||||
const Reflection::EnumPropDescriptor<AnimationTrack, KeyframeSequence::Priority> prop_Priority(
|
||||
"Priority", category_Data, &AnimationTrack::getPriority, &AnimationTrack::setPriority);
|
||||
|
||||
static Reflection::EventDesc<AnimationTrack, void(std::string)> event_Keyframe(
|
||||
&AnimationTrack::keyframeReachedSignal, "KeyframeReached", "keyframeName");
|
||||
static Reflection::EventDesc<AnimationTrack, void()> event_Stopped(&AnimationTrack::stoppedSignal, "Stopped");
|
||||
REFLECTION_END();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
// FRONTEND AND BACKEND
|
||||
|
||||
AnimationTrack::AnimationTrack(shared_ptr<AnimationTrackState> animationTrackState, weak_ptr<Animator> animator, shared_ptr<Animation> anim)
|
||||
: DescribedNonCreatable<AnimationTrack, Instance, sAnimationTrack>()
|
||||
, animationTrackState(animationTrackState)
|
||||
, animator(animator)
|
||||
, animation(anim)
|
||||
{
|
||||
setName(sAnimationTrack);
|
||||
|
||||
keyframeReachedConnection = animationTrackState->keyframeReachedSignal.connect(boost::bind(&AnimationTrack::forwardKeyframeReached, this, _1));
|
||||
stoppedConnection = animationTrackState->stoppedSignal.connect(boost::bind(&AnimationTrack::forwardStopped, this));
|
||||
|
||||
lockParent();
|
||||
}
|
||||
AnimationTrack::~AnimationTrack()
|
||||
{
|
||||
if (keyframeReachedConnection.connected())
|
||||
keyframeReachedConnection.disconnect();
|
||||
|
||||
if (stoppedConnection.connected())
|
||||
stoppedConnection.disconnect();
|
||||
}
|
||||
|
||||
Animation* AnimationTrack::getAnimation() const
|
||||
{
|
||||
return animation.get();
|
||||
}
|
||||
|
||||
float AnimationTrack::getLength() const
|
||||
{
|
||||
return animationTrackState->getDuration();
|
||||
}
|
||||
|
||||
bool AnimationTrack::getIsPlaying() const
|
||||
{
|
||||
return animationTrackState->getIsPlaying();
|
||||
}
|
||||
|
||||
KeyframeSequence::Priority AnimationTrack::getPriority() const
|
||||
{
|
||||
return animationTrackState->getPriority();
|
||||
}
|
||||
|
||||
void AnimationTrack::setPriority(KeyframeSequence::Priority priority)
|
||||
{
|
||||
animationTrackState->setPriority(priority);
|
||||
}
|
||||
|
||||
void AnimationTrack::forwardKeyframeReached(std::string keyFrame)
|
||||
{
|
||||
keyframeReachedSignal(keyFrame);
|
||||
}
|
||||
|
||||
void AnimationTrack::forwardStopped()
|
||||
{
|
||||
stoppedSignal();
|
||||
}
|
||||
|
||||
void AnimationTrack::play(float fadeTime, float weight, float speed)
|
||||
{
|
||||
localPlay(fadeTime, weight, speed);
|
||||
|
||||
if (shared_ptr<Animator> safeAnimator = animator.lock())
|
||||
{
|
||||
shared_ptr<Instance> me = shared_from<Instance>(this);
|
||||
safeAnimator->replicateAnimationPlay(animation->getAssetId(), fadeTime, weight, speed, me);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationTrack::localPlay(float fadeTime, float weight, float speed)
|
||||
{
|
||||
if (shared_ptr<Animator> safeAnimator = animator.lock())
|
||||
{
|
||||
safeAnimator->reloadAnimation(animationTrackState);
|
||||
animationTrackState->play(fadeTime, weight, speed);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationTrack::stop(float fadeTime)
|
||||
{
|
||||
localStop(fadeTime);
|
||||
|
||||
if (shared_ptr<Animator> safeAnimator = animator.lock())
|
||||
{
|
||||
safeAnimator->replicateAnimationStop(animation->getAssetId(), fadeTime);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationTrack::localStop(float fadeTime)
|
||||
{
|
||||
animationTrackState->stop(fadeTime);
|
||||
}
|
||||
|
||||
void AnimationTrack::adjustWeight(float weight, float fadeTime)
|
||||
{
|
||||
if (shared_ptr<Animator> safeAnimator = animator.lock())
|
||||
{
|
||||
safeAnimator->reloadAnimation(animationTrackState);
|
||||
animationTrackState->adjustWeight(weight, fadeTime);
|
||||
}
|
||||
}
|
||||
void AnimationTrack::adjustSpeed(float speed)
|
||||
{
|
||||
double oldSpeed = animationTrackState->getSpeed();
|
||||
localAdjustSpeed(speed);
|
||||
|
||||
if (oldSpeed != animationTrackState->getSpeed())
|
||||
{
|
||||
if (shared_ptr<Animator> safeAnimator = animator.lock())
|
||||
{
|
||||
safeAnimator->replicateAnimationSpeed(animation->getAssetId(), speed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationTrack::localAdjustSpeed(float speed)
|
||||
{
|
||||
if (shared_ptr<Animator> safeAnimator = animator.lock())
|
||||
{
|
||||
safeAnimator->reloadAnimation(animationTrackState);
|
||||
animationTrackState->adjustSpeed(speed);
|
||||
}
|
||||
}
|
||||
|
||||
const std::string AnimationTrack::getAnimationName() const
|
||||
{
|
||||
if (animation)
|
||||
{
|
||||
return animation->getName();
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
double AnimationTrack::getGameTime() const
|
||||
{
|
||||
shared_ptr<Animator> safeAnimator = animator.lock();
|
||||
if (safeAnimator)
|
||||
{
|
||||
return safeAnimator->getGameTime();
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
double AnimationTrack::getTimePosition() const
|
||||
{
|
||||
return animationTrackState->getDurationClampedKeyframeTime(animationTrackState->getKeyframeAtTime(getGameTime()));
|
||||
}
|
||||
|
||||
void AnimationTrack::setTimePosition(double timePosition)
|
||||
{
|
||||
double oldTime = getTimePosition();
|
||||
localSetTimePosition(timePosition);
|
||||
|
||||
if (oldTime != getTimePosition())
|
||||
{
|
||||
shared_ptr<Animator> safeAnimator = animator.lock();
|
||||
if (safeAnimator)
|
||||
{
|
||||
safeAnimator->replicateAnimationTimePosition(animation->getAssetId(), timePosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationTrack::localSetTimePosition(double timePosition)
|
||||
{
|
||||
double clampedKeyframeTime = animationTrackState->getDurationClampedKeyframeTime(timePosition);
|
||||
animationTrackState->setKeyframeAtTime(getGameTime(), clampedKeyframeTime);
|
||||
animationTrackState->resetKeyframeReachedDetection(clampedKeyframeTime);
|
||||
}
|
||||
|
||||
double AnimationTrack::getTimeOfKeyframe(std::string keyframeName)
|
||||
{
|
||||
const KeyframeSequence* kfs = animationTrackState->getKeyframeSequence();
|
||||
for (unsigned int index = 0; index < kfs->numChildren(); index++)
|
||||
{
|
||||
if (kfs->getChild(index)->getName() == keyframeName)
|
||||
{
|
||||
const Keyframe* keyframe = Instance::fastDynamicCast<const Keyframe>(kfs->getChild(index));
|
||||
AYAASSERT(keyframe);
|
||||
return keyframe->getTime();
|
||||
}
|
||||
}
|
||||
throw Aya::runtime_error("Could not find a keyframe by that name!");
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
65
engine/app/src/DataModel/AnimationTrack.hpp
Normal file
65
engine/app/src/DataModel/AnimationTrack.hpp
Normal file
@@ -0,0 +1,65 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Tree/Instance.hpp"
|
||||
#include "DataModel/KeyframeSequence.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
class Animator;
|
||||
class AnimationTrackState;
|
||||
class Animation;
|
||||
extern const char* const sAnimationTrack;
|
||||
|
||||
// A controller class that wraps an AnimationTrackState and allows for interaction with Lua
|
||||
class AnimationTrack : public DescribedNonCreatable<AnimationTrack, Instance, sAnimationTrack>
|
||||
{
|
||||
protected:
|
||||
// Keeps animationTrackState alive (in case we need to restart it)
|
||||
shared_ptr<AnimationTrackState> animationTrackState;
|
||||
|
||||
// Keeps a weak ptr to our animator, so we can restart an animation if it has become stopped
|
||||
weak_ptr<Animator> animator;
|
||||
|
||||
shared_ptr<Animation> animation;
|
||||
|
||||
Aya::signals::connection keyframeReachedConnection;
|
||||
Aya::signals::connection stoppedConnection;
|
||||
|
||||
void forwardKeyframeReached(std::string);
|
||||
void forwardStopped();
|
||||
|
||||
double getGameTime() const;
|
||||
|
||||
public:
|
||||
AnimationTrack(shared_ptr<AnimationTrackState> animationTrackState, weak_ptr<Animator> animator, shared_ptr<Animation> anim);
|
||||
~AnimationTrack();
|
||||
|
||||
void play(float fadeTime, float weight, float speed);
|
||||
void localPlay(float fadeTime, float weight, float speed);
|
||||
void stop(float fadeTime);
|
||||
void localStop(float fadeTime);
|
||||
void adjustWeight(float weight, float fadeTime);
|
||||
void adjustSpeed(float speed);
|
||||
void localAdjustSpeed(float speed);
|
||||
|
||||
float getLength() const;
|
||||
bool getIsPlaying() const;
|
||||
Animation* getAnimation() const;
|
||||
|
||||
double getTimePosition() const;
|
||||
void setTimePosition(double timePosition);
|
||||
void localSetTimePosition(double timePosition);
|
||||
|
||||
double getTimeOfKeyframe(std::string keyframeName);
|
||||
|
||||
KeyframeSequence::Priority getPriority() const;
|
||||
void setPriority(KeyframeSequence::Priority priority);
|
||||
|
||||
const std::string getAnimationName() const;
|
||||
|
||||
Aya::signal<void(std::string)> keyframeReachedSignal;
|
||||
Aya::signal<void()> stoppedSignal;
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
386
engine/app/src/DataModel/AnimationTrackState.cpp
Normal file
386
engine/app/src/DataModel/AnimationTrackState.cpp
Normal file
@@ -0,0 +1,386 @@
|
||||
|
||||
|
||||
|
||||
#include "DataModel/AnimationTrackState.hpp"
|
||||
#include "DataModel/Animation.hpp"
|
||||
#include "DataModel/Animator.hpp"
|
||||
#include "DataModel/KeyframeSequence.hpp"
|
||||
#include "Tree/Service.hpp"
|
||||
#include "Utility/RunStateOwner.hpp"
|
||||
#include "DataModel/Animation.hpp"
|
||||
#include "DataModel/KeyframeSequenceProvider.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
const char* const sAnimationTrackState = "AnimationTrackState";
|
||||
static const float autoStopFadeTime = 0.3f;
|
||||
|
||||
|
||||
static Reflection::RemoteEventDesc<AnimationTrackState, void(float, float, float, float)> event_play(&AnimationTrackState::internalPlaySignal,
|
||||
"PlayAnimation", "gameTime", "fadeTime", "weight", "speed", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY,
|
||||
Reflection::RemoteEventCommon::BROADCAST);
|
||||
static Reflection::RemoteEventDesc<AnimationTrackState, void(float, float)> event_stop(&AnimationTrackState::internalStopSignal, "StopAnimation",
|
||||
"gameTime", "fadeTime", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
|
||||
static Reflection::RemoteEventDesc<AnimationTrackState, void(float, float, float)> event_adjustWeight(
|
||||
&AnimationTrackState::internalAdjustWeightSignal, "AdjustAnimationWeight", "gameTime", "weight", "fadeTime", Security::None,
|
||||
Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
|
||||
static Reflection::RemoteEventDesc<AnimationTrackState, void(float, float)> event_adjustSpeed(&AnimationTrackState::internalAdjustSpeedSignal,
|
||||
"AdjustAnimation", "gameTime", "speed", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
|
||||
|
||||
static Reflection::RemoteEventDesc<AnimationTrackState, void(std::string)> event_Keyframe(&AnimationTrackState::keyframeReachedSignal,
|
||||
"KeyframeReached", "keyframeName", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
|
||||
static Reflection::RemoteEventDesc<AnimationTrackState, void()> event_Stopped(&AnimationTrackState::stoppedSignal, "Stopped", Security::None,
|
||||
Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
|
||||
REFLECTION_END();
|
||||
|
||||
AnimationTrackState::AnimationTrackState(shared_ptr<const KeyframeSequence> keyframeSequence, weak_ptr<const Animator> animator)
|
||||
: DescribedNonCreatable<AnimationTrackState, Instance, sAnimationTrackState>()
|
||||
, startTime(0.0f)
|
||||
, fadeStartTime(0.0f)
|
||||
, fadeStartWeight(0.0f)
|
||||
, fadeEndTime(0.0f)
|
||||
, fadeEndWeight(0.0f)
|
||||
, speed(1.0f)
|
||||
, phase(0.0f)
|
||||
, keyframeSequence(keyframeSequence)
|
||||
, animator(animator)
|
||||
, preKeyframe(-1)
|
||||
, isPlaying(false)
|
||||
, priority(KeyframeSequence::CORE)
|
||||
, priorityOverridden(false)
|
||||
{
|
||||
setName(sAnimationTrackState);
|
||||
|
||||
internalPlaySignal.connect(boost::bind(&AnimationTrackState::onPlay, this, _1, _2, _3, _4));
|
||||
internalStopSignal.connect(boost::bind(&AnimationTrackState::onStop, this, _1, _2));
|
||||
internalAdjustWeightSignal.connect(boost::bind(&AnimationTrackState::onAdjustWeight, this, _1, _2, _3));
|
||||
internalAdjustSpeedSignal.connect(boost::bind(&AnimationTrackState::onAdjustSpeed, this, _1, _2));
|
||||
|
||||
lockParent();
|
||||
}
|
||||
|
||||
void AnimationTrackState::setAnimationTrack(shared_ptr<AnimationTrack> animationTrackIn)
|
||||
{
|
||||
animationTrack = animationTrackIn;
|
||||
}
|
||||
|
||||
bool AnimationTrackState::isStopped(double time)
|
||||
{
|
||||
return (time >= fadeEndTime) && G3D::fuzzyEq(fadeEndWeight, 0);
|
||||
}
|
||||
double AnimationTrackState::getGameTime()
|
||||
{
|
||||
if (shared_ptr<const Animator> safeAnimator = animator.lock())
|
||||
{
|
||||
return safeAnimator->getGameTime();
|
||||
}
|
||||
// We've lost our animator, shut.it.down
|
||||
keyframeSequence.reset();
|
||||
return 0.0f;
|
||||
}
|
||||
double AnimationTrackState::getWeightAtTime(double time)
|
||||
{
|
||||
if (time >= fadeEndTime)
|
||||
{
|
||||
return fadeEndWeight;
|
||||
}
|
||||
else if (time <= fadeStartTime)
|
||||
{
|
||||
return fadeStartWeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
// lerp between two.
|
||||
double interval = (fadeEndTime - fadeStartTime);
|
||||
return ((time - fadeStartTime) / interval) * fadeEndWeight + ((fadeEndTime - time) / interval) * fadeStartWeight;
|
||||
}
|
||||
}
|
||||
|
||||
double AnimationTrackState::getKeyframeAtTime(double time)
|
||||
{
|
||||
if (!inReverse())
|
||||
{
|
||||
return (time - startTime) * speed + phase;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (getDuration() - (time - startTime) * -speed) + phase;
|
||||
}
|
||||
}
|
||||
|
||||
float AnimationTrackState::getDuration()
|
||||
{
|
||||
if (const KeyframeSequence* pSequence = getKeyframeSequence())
|
||||
{
|
||||
return pSequence->getDuration();
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
KeyframeSequence::Priority AnimationTrackState::getPriority() const
|
||||
{
|
||||
if (priorityOverridden)
|
||||
{
|
||||
return priority;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (const KeyframeSequence* sequence = getKeyframeSequence())
|
||||
{
|
||||
return sequence->getPriority();
|
||||
}
|
||||
else
|
||||
{
|
||||
return KeyframeSequence::CORE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationTrackState::setPriority(KeyframeSequence::Priority priorityIn)
|
||||
{
|
||||
priorityOverridden = true;
|
||||
priority = priorityIn;
|
||||
}
|
||||
|
||||
void AnimationTrackState::setKeyframeAtTime(double gameTime, double keyframetime)
|
||||
{
|
||||
double delta = keyframetime - getKeyframeAtTime(gameTime);
|
||||
phase += delta;
|
||||
}
|
||||
|
||||
void AnimationTrackState::onPlay(float gameTime, float fadeTime, float weight, float speed)
|
||||
{
|
||||
if (gameTime >= startTime)
|
||||
{
|
||||
phase = 0;
|
||||
startTime = gameTime;
|
||||
fadeStartTime = startTime;
|
||||
fadeStartWeight = 0.0;
|
||||
fadeEndTime = startTime + fadeTime;
|
||||
fadeEndWeight = weight;
|
||||
this->speed = speed;
|
||||
|
||||
lastKeyframeTime = 0;
|
||||
preKeyframe = -1;
|
||||
|
||||
isPlaying = true;
|
||||
|
||||
// automated animation detection does not detect the first keyframe if the animation is running in reverse
|
||||
if (inReverse())
|
||||
{
|
||||
const KeyframeSequence* kfs = getKeyframeSequence();
|
||||
if (kfs->numChildren() > 0)
|
||||
{
|
||||
if (kfs->getChild(kfs->numChildren() - 1))
|
||||
{
|
||||
event_Keyframe.fireAndReplicateEvent(this, kfs->getChild(kfs->numChildren() - 1)->getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationTrackState::play(float fadeTime, float weight, float speed)
|
||||
{
|
||||
event_play.fireAndReplicateEvent(this, (float)getGameTime(), fadeTime, weight, speed);
|
||||
}
|
||||
|
||||
void AnimationTrackState::onStop(float gameTime, float fadeTime)
|
||||
{
|
||||
fadeStartTime = gameTime;
|
||||
fadeStartWeight = getWeightAtTime(gameTime);
|
||||
fadeEndTime = gameTime + fadeTime;
|
||||
fadeEndWeight = 0.0;
|
||||
event_Stopped.fireAndReplicateEvent(this);
|
||||
|
||||
isPlaying = false;
|
||||
}
|
||||
|
||||
void AnimationTrackState::stop(float fadeTime)
|
||||
{
|
||||
event_stop.fireAndReplicateEvent(this, (float)getGameTime(), fadeTime);
|
||||
}
|
||||
|
||||
void AnimationTrackState::onAdjustWeight(float gameTime, float weight, float fadeTime)
|
||||
{
|
||||
fadeStartTime = gameTime;
|
||||
fadeStartWeight = getWeightAtTime(gameTime);
|
||||
fadeEndTime = gameTime + fadeTime;
|
||||
fadeEndWeight = weight;
|
||||
}
|
||||
|
||||
void AnimationTrackState::onAdjustSpeed(float gameTime, float speed)
|
||||
{
|
||||
if (speed != this->speed)
|
||||
{
|
||||
// if we reversed directions, we need to reset the preKeyframe
|
||||
if ((this->speed >= 0 && speed < 0) || (this->speed < 0 && speed >= 0))
|
||||
{
|
||||
preKeyframe = -1;
|
||||
}
|
||||
|
||||
// must adjust phase to prevent skipping.
|
||||
double keyframetime = getKeyframeAtTime(gameTime);
|
||||
this->speed = speed;
|
||||
setKeyframeAtTime(gameTime, keyframetime);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationTrackState::adjustWeight(float weight, float fadeTime)
|
||||
{
|
||||
event_adjustWeight.fireAndReplicateEvent(this, (float)getGameTime(), weight, fadeTime);
|
||||
}
|
||||
|
||||
void AnimationTrackState::adjustSpeed(float speed)
|
||||
{
|
||||
event_adjustSpeed.fireAndReplicateEvent(this, (float)getGameTime(), speed);
|
||||
}
|
||||
|
||||
void AnimationTrackState::triggerKeyframeReachedSignal(const shared_ptr<Instance>& child, double minKeyframeTime, double maxKeyframeTime)
|
||||
{
|
||||
Keyframe* kf = Instance::fastDynamicCast<Keyframe>(child.get());
|
||||
if (kf)
|
||||
{
|
||||
if (kf->getTime() > minKeyframeTime && kf->getTime() <= maxKeyframeTime)
|
||||
{
|
||||
event_Keyframe.fireAndReplicateEvent(this, kf->getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AnimationTrackState::inReverse()
|
||||
{
|
||||
return (speed < 0);
|
||||
}
|
||||
|
||||
double AnimationTrackState::getDurationClampedKeyframeTime(double keyframeTime)
|
||||
{
|
||||
double duration = getDuration();
|
||||
if (duration > 0.0)
|
||||
{
|
||||
if (keyframeTime < 0)
|
||||
{
|
||||
int durationsOff = (int)(fabs(keyframeTime) / duration) + 63;
|
||||
keyframeTime = keyframeTime + durationsOff * duration;
|
||||
}
|
||||
else if (keyframeTime > duration)
|
||||
{
|
||||
int durationsOff = (int)(keyframeTime / duration);
|
||||
keyframeTime = keyframeTime - durationsOff * duration;
|
||||
}
|
||||
}
|
||||
return keyframeTime;
|
||||
}
|
||||
|
||||
void AnimationTrackState::resetKeyframeReachedDetection(double keyframeTime)
|
||||
{
|
||||
lastKeyframeTime = keyframeTime;
|
||||
preKeyframe = -1;
|
||||
}
|
||||
|
||||
void AnimationTrackState::step(std::vector<PoseAccumulator>& jointposes, double time)
|
||||
{
|
||||
double keyframetime = getKeyframeAtTime(time);
|
||||
float trackweight = (float)getWeightAtTime(time);
|
||||
|
||||
const KeyframeSequence* kfs = getKeyframeSequence();
|
||||
double normKeyframeTime = keyframetime;
|
||||
float duration = getKeyframeSequence()->getDuration();
|
||||
|
||||
kfs->apply(jointposes, lastKeyframeTime, keyframetime, trackweight);
|
||||
|
||||
if (!kfs->getLoop()) // play one time. give stop command after trigger of last keyframe.
|
||||
{
|
||||
// determine if we are finished with the animation
|
||||
bool doneWithAnimation;
|
||||
if (!inReverse())
|
||||
doneWithAnimation = (lastKeyframeTime < duration && keyframetime >= duration);
|
||||
else
|
||||
doneWithAnimation = (lastKeyframeTime > 0 && keyframetime <= 0);
|
||||
if (duration <= 0)
|
||||
doneWithAnimation = false;
|
||||
|
||||
if (doneWithAnimation)
|
||||
{
|
||||
// fire signal for the last frame
|
||||
if (!inReverse())
|
||||
event_Keyframe.fireAndReplicateEvent(this, kfs->getChild(kfs->numChildren() - 1)->getName());
|
||||
else
|
||||
event_Keyframe.fireAndReplicateEvent(this, kfs->getChild(0)->getName());
|
||||
|
||||
// should we use the internal event? or the external one?
|
||||
// we will play it safe and use the internal one. it is easier to notice a problem with a non-stopped animation than with a
|
||||
// double fire.
|
||||
onStop((float)time, autoStopFadeTime);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// find out where in the looping animation we are -- even if we're moving backwards
|
||||
normKeyframeTime = getDurationClampedKeyframeTime(keyframetime);
|
||||
}
|
||||
|
||||
detectKeyframeReached(normKeyframeTime, getDurationClampedKeyframeTime(lastKeyframeTime));
|
||||
|
||||
lastKeyframeTime = keyframetime;
|
||||
}
|
||||
|
||||
void AnimationTrackState::detectKeyframeReached(double animationTime, double lastAnimationTime)
|
||||
{
|
||||
if (!isPlaying)
|
||||
return;
|
||||
|
||||
const KeyframeSequence* kfs = getKeyframeSequence();
|
||||
int numKf = kfs->numChildren();
|
||||
|
||||
if (numKf == 0)
|
||||
return;
|
||||
|
||||
// did we just loop? if so, fire the keyframes relevant. of course, only if the animation does loop
|
||||
if (kfs->getLoop())
|
||||
{
|
||||
if (!inReverse())
|
||||
{
|
||||
if (lastAnimationTime > animationTime)
|
||||
{
|
||||
event_Keyframe.fireAndReplicateEvent(this, kfs->getChild(kfs->numChildren() - 1)->getName());
|
||||
event_Keyframe.fireAndReplicateEvent(this, kfs->getChild(0)->getName());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (animationTime > lastAnimationTime)
|
||||
{
|
||||
event_Keyframe.fireAndReplicateEvent(this, kfs->getChild(0)->getName());
|
||||
event_Keyframe.fireAndReplicateEvent(this, kfs->getChild(kfs->numChildren() - 1)->getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now we need to check the midway keyframes
|
||||
for (int index = 0; index < numKf; index++)
|
||||
{
|
||||
const Keyframe* keyframe = Instance::fastDynamicCast<const Keyframe>(kfs->getChild(index));
|
||||
AYAASSERT(keyframe);
|
||||
|
||||
bool passedKeyframe;
|
||||
if (!inReverse())
|
||||
passedKeyframe = (lastAnimationTime <= keyframe->getTime() && keyframe->getTime() < animationTime);
|
||||
else
|
||||
passedKeyframe = (lastAnimationTime >= keyframe->getTime() && keyframe->getTime() > animationTime);
|
||||
|
||||
if (passedKeyframe)
|
||||
{
|
||||
if (preKeyframe == index)
|
||||
continue;
|
||||
|
||||
event_Keyframe.fireAndReplicateEvent(this, keyframe->getName());
|
||||
preKeyframe = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
112
engine/app/src/DataModel/AnimationTrackState.hpp
Normal file
112
engine/app/src/DataModel/AnimationTrackState.hpp
Normal file
@@ -0,0 +1,112 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Tree/Instance.hpp"
|
||||
#include "Utility/ContentId.hpp"
|
||||
#include "DataModel/KeyframeSequence.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
extern const char* const sAnimationTrackState;
|
||||
|
||||
class KeyframeSequence;
|
||||
class Animator;
|
||||
class AnimationTrack;
|
||||
struct PoseAccumulator;
|
||||
|
||||
class AnimationTrackState : public DescribedNonCreatable<AnimationTrackState, Instance, sAnimationTrackState>
|
||||
{
|
||||
protected:
|
||||
// Either set directly for solo/debugging or replicated by the AssetId
|
||||
shared_ptr<const KeyframeSequence> keyframeSequence;
|
||||
|
||||
shared_ptr<AnimationTrack> animationTrack;
|
||||
|
||||
int preKeyframe;
|
||||
|
||||
// Keep a weak ptr to our parent
|
||||
weak_ptr<const Animator> animator;
|
||||
|
||||
double startTime; // reset by the "play" function.
|
||||
double speed;
|
||||
double phase; // speed-independent adjustment to keyframetime.
|
||||
double fadeStartTime;
|
||||
double fadeStartWeight;
|
||||
double fadeEndTime;
|
||||
double fadeEndWeight;
|
||||
|
||||
bool isPlaying;
|
||||
|
||||
double getGameTime(); // helper
|
||||
|
||||
void onPlay(float gameTime, float fadeTime, float weight, float speed);
|
||||
void onStop(float gameTime, float fadeTime);
|
||||
void onAdjustWeight(float gameTime, float weight, float fadeTime);
|
||||
void onAdjustSpeed(float gameTime, float speed);
|
||||
|
||||
bool inReverse();
|
||||
void detectKeyframeReached(double animationTime, double lastAnimationTime);
|
||||
|
||||
KeyframeSequence::Priority priority;
|
||||
bool priorityOverridden;
|
||||
|
||||
// helper for step()
|
||||
void triggerKeyframeReachedSignal(const shared_ptr<Instance>& child, double minKeyframeTime, double maxKeyframeTime);
|
||||
|
||||
protected:
|
||||
double lastKeyframeTime; // keep track of last played frame for keyframe events.
|
||||
public:
|
||||
AnimationTrackState(shared_ptr<const KeyframeSequence> keyframeSequence, weak_ptr<const Animator> animator);
|
||||
~AnimationTrackState() {}
|
||||
void play(float fadeTime, float weight, float speed);
|
||||
void stop(float fadeTime);
|
||||
void adjustWeight(float weight, float fadeTime);
|
||||
void adjustSpeed(float speed);
|
||||
|
||||
// Should be private
|
||||
Aya::remote_signal<void(float, float, float, float)> internalPlaySignal;
|
||||
Aya::remote_signal<void(float, float)> internalStopSignal;
|
||||
Aya::remote_signal<void(float, float, float)> internalAdjustWeightSignal;
|
||||
Aya::remote_signal<void(float, float)> internalAdjustSpeedSignal;
|
||||
Aya::remote_signal<void(std::string)> keyframeReachedSignal;
|
||||
Aya::remote_signal<void()> stoppedSignal;
|
||||
|
||||
const KeyframeSequence* getKeyframeSequence() const
|
||||
{
|
||||
return keyframeSequence.get();
|
||||
};
|
||||
void setKeyframeSequence(shared_ptr<KeyframeSequence>);
|
||||
|
||||
double getWeightAtTime(double time);
|
||||
double getKeyframeAtTime(double time);
|
||||
void setKeyframeAtTime(double gameTime, double keyframeTime); // adjust phase to get animation on a specific keyframe.
|
||||
|
||||
double getSpeed()
|
||||
{
|
||||
return speed;
|
||||
}
|
||||
float getDuration();
|
||||
|
||||
bool isStopped(double time);
|
||||
bool getIsPlaying() const
|
||||
{
|
||||
return isPlaying;
|
||||
};
|
||||
|
||||
double getDurationClampedKeyframeTime(double keyframeTime);
|
||||
|
||||
KeyframeSequence::Priority getPriority() const;
|
||||
void setPriority(KeyframeSequence::Priority priority);
|
||||
|
||||
void resetKeyframeReachedDetection(double keyframeTime);
|
||||
|
||||
void step(std::vector<PoseAccumulator>& jointposes, double time);
|
||||
|
||||
void setAnimationTrack(shared_ptr<AnimationTrack> animationTrackIn);
|
||||
shared_ptr<AnimationTrack> getAnimationTrack() const
|
||||
{
|
||||
return animationTrack;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
553
engine/app/src/DataModel/Animator.cpp
Normal file
553
engine/app/src/DataModel/Animator.cpp
Normal file
@@ -0,0 +1,553 @@
|
||||
|
||||
|
||||
|
||||
|
||||
#include "DataModel/Animator.hpp"
|
||||
#include "DataModel/Animation.hpp"
|
||||
#include "DataModel/AnimationTrack.hpp"
|
||||
#include "DataModel/AnimationTrackState.hpp"
|
||||
#include "DataModel/JointInstance.hpp"
|
||||
#include "DataModel/Workspace.hpp"
|
||||
#include "World/SendPhysics.hpp"
|
||||
#include "World/World.hpp"
|
||||
#include "World/Primitive.hpp"
|
||||
#include "World/Assembly.hpp"
|
||||
#include "Players.hpp"
|
||||
#include "NetworkOwner.hpp"
|
||||
|
||||
#include "Humanoid/Humanoid.hpp"
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
#include "DataModel/AnimatableRootJoint.hpp"
|
||||
#include "DataModel/AnimationController.hpp"
|
||||
|
||||
#include "Profiler.hpp"
|
||||
|
||||
DYNAMIC_FASTFLAGVARIABLE(ErrorOnFailedToLoadAnim, false)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
const char* const sAnimator = "Animator";
|
||||
|
||||
|
||||
// Keep this interface in sync with the proxy one on Humanoid.
|
||||
// Returns an AnimationTrack
|
||||
static Reflection::BoundFuncDesc<Animator, shared_ptr<Instance>(shared_ptr<Instance>)> desc_LoadAnimation(
|
||||
&Animator::loadAnimation, "LoadAnimation", "animation", Security::None);
|
||||
|
||||
// animation state replication
|
||||
static Reflection::RemoteEventDesc<Animator, void(ContentId, float, float, float)> event_OnPlay(&Animator::onPlaySignal, "OnPlay", "animation",
|
||||
"fadeTime", "weight", "speed", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
|
||||
static Reflection::RemoteEventDesc<Animator, void(ContentId, float)> event_OnStop(&Animator::onStopSignal, "OnStop", "animation", "fadeTime",
|
||||
Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
|
||||
static Reflection::RemoteEventDesc<Animator, void(ContentId, float)> event_OnAdjustSpeed(&Animator::onAdjustSpeedSignal, "OnAdjustSpeed", "animation",
|
||||
"speed", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
|
||||
static Reflection::RemoteEventDesc<Animator, void(ContentId, float)> event_OnSetTimePosition(&Animator::onSetTimePositionSignal, "OnSetTimePosition",
|
||||
"animation", "timePosition", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
|
||||
REFLECTION_END();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
// FRONTEND AND BACKEND
|
||||
|
||||
Animator::Animator()
|
||||
: DescribedCreatable<Animator, Instance, sAnimator>()
|
||||
{
|
||||
setName(sAnimator);
|
||||
|
||||
FASTLOG1(FLog::ISteppedLifetime, "Animator created - %p", this);
|
||||
|
||||
onPlaySignal.connect(boost::bind(&Animator::onPlay, this, _1, _2, _3, _4));
|
||||
onStopSignal.connect(boost::bind(&Animator::onStop, this, _1, _2));
|
||||
onAdjustSpeedSignal.connect(boost::bind(&Animator::onAdjustSpeed, this, _1, _2));
|
||||
onSetTimePositionSignal.connect(boost::bind(&Animator::onSetTimePosition, this, _1, _2));
|
||||
}
|
||||
|
||||
Animator::Animator(Instance* replicatingContainer)
|
||||
: DescribedCreatable<Animator, Instance, sAnimator>()
|
||||
{
|
||||
shared_ptr<Instance> container = shared_from(replicatingContainer);
|
||||
|
||||
setName(sAnimator);
|
||||
|
||||
FASTLOG1(FLog::ISteppedLifetime, "Animator created - %p", this);
|
||||
|
||||
onPlaySignal.connect(boost::bind(&Animator::onPlay, this, _1, _2, _3, _4));
|
||||
onStopSignal.connect(boost::bind(&Animator::onStop, this, _1, _2));
|
||||
onAdjustSpeedSignal.connect(boost::bind(&Animator::onAdjustSpeed, this, _1, _2));
|
||||
onSetTimePositionSignal.connect(boost::bind(&Animator::onSetTimePosition, this, _1, _2));
|
||||
}
|
||||
|
||||
Animator::~Animator()
|
||||
{
|
||||
FASTLOG1(FLog::ISteppedLifetime, "Animator destroyed - %p", this);
|
||||
}
|
||||
|
||||
Instance* Animator::getRootInstance()
|
||||
{
|
||||
if (getParent())
|
||||
{
|
||||
return getParent()->getParent();
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Animator::setupClumpChangedListener(Instance* rootInstance)
|
||||
{
|
||||
if (PartInstance* p = rootInstance->findFirstDescendantOfType<PartInstance>())
|
||||
{
|
||||
if (shared_ptr<PartInstance> rootPart = Instance::fastSharedDynamicCast<PartInstance>(p->getRootPart()))
|
||||
clumpChangedConnection = rootPart->onDemandWrite()->clumpChangedSignal.connect(boost::bind(&Animator::onEvent_ClumpChanged, this, _1));
|
||||
}
|
||||
}
|
||||
|
||||
void Animator::reloadAnimation(shared_ptr<AnimationTrackState> animationTrackState)
|
||||
{
|
||||
if (animationTrackState->getKeyframeSequence())
|
||||
{
|
||||
if (std::find(activeAnimations.begin(), activeAnimations.end(), animationTrackState) == activeAnimations.end())
|
||||
{
|
||||
activeAnimations.push_back(animationTrackState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float Animator::getGameTime() const
|
||||
{
|
||||
RunService* runservice = NULL;
|
||||
runservice = ServiceProvider::find<RunService>(getParent());
|
||||
|
||||
if (runservice)
|
||||
{
|
||||
return (float)runservice->gameTime();
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
shared_ptr<Instance> Animator::loadAnimation(shared_ptr<Instance> instance)
|
||||
{
|
||||
shared_ptr<Animation> animation = Instance::fastSharedDynamicCast<Animation>(instance);
|
||||
|
||||
if (animation)
|
||||
{
|
||||
shared_ptr<const KeyframeSequence> kfs = animation->getKeyframeSequence(getParent());
|
||||
if (!kfs)
|
||||
{
|
||||
StandardOut::singleton()->printf(MESSAGE_WARNING, "Object must be in Workspace before loading animation.");
|
||||
}
|
||||
|
||||
shared_ptr<Animator> sharedThis = shared_from(this);
|
||||
shared_ptr<AnimationTrackState> trackState(Creatable<Instance>::create<AnimationTrackState>(kfs, sharedThis));
|
||||
shared_ptr<AnimationTrack> track(Creatable<Instance>::create<AnimationTrack>(trackState, sharedThis, animation));
|
||||
trackState->setAnimationTrack(track);
|
||||
trackState->setName(instance->getName());
|
||||
track->setName(instance->getName());
|
||||
return track;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Aya::runtime_error("Argument error: must be an Animation object");
|
||||
}
|
||||
}
|
||||
|
||||
void Animator::onTrackStepped(
|
||||
shared_ptr<AnimationTrackState> track, double time, KeyframeSequence::Priority priority, std::vector<PoseAccumulator>* poses)
|
||||
{
|
||||
if (track->getPriority() == priority)
|
||||
{
|
||||
if (poses->size() == 0) // delay creation
|
||||
{
|
||||
poses->reserve(animatableJoints.size());
|
||||
for (size_t i = 0; i < animatableJoints.size(); ++i)
|
||||
{
|
||||
poses->push_back(PoseAccumulator(animatableJoints[i]));
|
||||
}
|
||||
}
|
||||
|
||||
track->step(*poses, time);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Animator::appendAnimatableJointsRec(shared_ptr<Instance> instance, shared_ptr<Instance> exclude)
|
||||
{
|
||||
if (instance == exclude)
|
||||
return;
|
||||
|
||||
Motor* joint = Instance::fastDynamicCast<Motor>(instance.get());
|
||||
if (joint)
|
||||
{
|
||||
joint->setIsAnimatedJoint(true);
|
||||
animatableJoints.push_back(JointPair(instance, joint));
|
||||
}
|
||||
}
|
||||
|
||||
void resetJointFlag(shared_ptr<Instance> instance)
|
||||
{
|
||||
Motor* joint = Instance::fastDynamicCast<Motor>(instance.get());
|
||||
if (joint)
|
||||
{
|
||||
joint->setIsAnimatedJoint(false);
|
||||
}
|
||||
}
|
||||
|
||||
void Animator::calcAnimatableJoints(Instance* rootInstance, shared_ptr<Instance> exclude)
|
||||
{
|
||||
animatableJoints.clear();
|
||||
|
||||
if (rootInstance)
|
||||
{
|
||||
rootInstance->visitDescendants(boost::bind(&resetJointFlag, _1));
|
||||
rootInstance->visitDescendants(boost::bind(&Animator::appendAnimatableJointsRec, this, _1, exclude));
|
||||
}
|
||||
}
|
||||
|
||||
void Animator::onStepped(const Stepped& event)
|
||||
{
|
||||
AYAPROFILER_SCOPE("Physics", "Animator::onStepped");
|
||||
|
||||
calcAnimatableJoints(getRootInstance());
|
||||
|
||||
if (animatableJoints.size() == 0 || activeAnimations.size() == 0)
|
||||
return;
|
||||
|
||||
std::vector<PoseAccumulator> core;
|
||||
std::vector<PoseAccumulator> idle;
|
||||
std::vector<PoseAccumulator> movement;
|
||||
std::vector<PoseAccumulator> action;
|
||||
|
||||
float gameTime = getGameTime();
|
||||
for (auto iter = activeAnimations.begin(); iter != activeAnimations.end();)
|
||||
{
|
||||
auto& animTrackState = *iter;
|
||||
if (!animTrackState->getKeyframeSequence() || animTrackState->isStopped(gameTime))
|
||||
{
|
||||
iter = activeAnimations.erase(iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
// checking to see if the server should take control of this object - NPC humanoid
|
||||
PartInstance* part = testForServerLockPart.get();
|
||||
if (part != NULL && activeAnimations.size() > 0 && Time::now<Time::Fast>() > serverLockTimer)
|
||||
{
|
||||
Primitive* prim = part->getPartPrimitive();
|
||||
Assembly* a = prim->getAssembly();
|
||||
if (a != NULL)
|
||||
{
|
||||
prim = a->getAssemblyPrimitive();
|
||||
part = PartInstance::fromPrimitive(prim);
|
||||
}
|
||||
part->resetNetworkOwnerTime(3.0f); // wait
|
||||
|
||||
part->setNetworkOwnerAndNotify(Network::NetworkOwner::Server());
|
||||
|
||||
serverLockTimer = Time::now<Time::Fast>() + Time::Interval(2.5f);
|
||||
}
|
||||
|
||||
std::for_each(activeAnimations.begin(), activeAnimations.end(),
|
||||
boost::bind(&Animator::onTrackStepped, this, _1, event.gameTime, KeyframeSequence::CORE, &core));
|
||||
std::for_each(activeAnimations.begin(), activeAnimations.end(),
|
||||
boost::bind(&Animator::onTrackStepped, this, _1, event.gameTime, KeyframeSequence::IDLE, &idle));
|
||||
std::for_each(activeAnimations.begin(), activeAnimations.end(),
|
||||
boost::bind(&Animator::onTrackStepped, this, _1, event.gameTime, KeyframeSequence::MOVEMENT, &movement));
|
||||
std::for_each(activeAnimations.begin(), activeAnimations.end(),
|
||||
boost::bind(&Animator::onTrackStepped, this, _1, event.gameTime, KeyframeSequence::ACTION, &action));
|
||||
|
||||
// fold all poses into idle
|
||||
if (idle.size() == 0)
|
||||
idle = core;
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < core.size(); ++i)
|
||||
{
|
||||
core[i].pose.weight *= idle[i].pose.maskWeight; // here is where we use the blendweight. make the blend is that.
|
||||
idle[i].pose = CachedPose::blendPoses(core[i].pose, idle[i].pose);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (idle.size() == 0)
|
||||
idle = movement;
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < movement.size(); ++i)
|
||||
{
|
||||
idle[i].pose.weight *= movement[i].pose.maskWeight; // here is where we use the blendweight. make the blend is that.
|
||||
idle[i].pose = CachedPose::blendPoses(idle[i].pose, movement[i].pose);
|
||||
}
|
||||
}
|
||||
|
||||
if (idle.size() == 0)
|
||||
idle = action;
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < action.size(); ++i)
|
||||
{
|
||||
idle[i].pose.weight *= action[i].pose.maskWeight; // here is where we use the blendweight. make the blend is that.
|
||||
idle[i].pose = CachedPose::blendPoses(idle[i].pose, action[i].pose);
|
||||
}
|
||||
}
|
||||
|
||||
// now, apply poses to joints;
|
||||
if (!idle.empty())
|
||||
{
|
||||
for (auto& poseAccumulator : idle)
|
||||
{
|
||||
auto pInst = poseAccumulator.joint.first.lock();
|
||||
if (pInst)
|
||||
{
|
||||
poseAccumulator.joint.second->applyPose(poseAccumulator.pose);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto& joint : animatableJoints)
|
||||
{
|
||||
auto pInst = joint.first.lock();
|
||||
if (pInst)
|
||||
{
|
||||
joint.second->applyPose(CachedPose());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*override*/ void Animator::onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
|
||||
{
|
||||
Super::onServiceProvider(oldProvider, newProvider);
|
||||
onServiceProviderIStepped(oldProvider, newProvider); // hooks up stepped
|
||||
|
||||
if (!newProvider)
|
||||
return;
|
||||
}
|
||||
|
||||
/*override*/ void Animator::verifySetParent(const Instance* instance) const
|
||||
{
|
||||
if (instance == NULL || fastDynamicCast<Humanoid>(instance) || fastDynamicCast<AnimationController>(instance))
|
||||
{
|
||||
Super::verifySetParent(instance);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Aya::runtime_error("Animator has to be placed under Humanoid or AnimationController!");
|
||||
}
|
||||
}
|
||||
|
||||
/*override*/ bool Animator::askAddChild(const Instance* instance) const
|
||||
{
|
||||
return Instance::fastDynamicCast<AnimationTrackState>(instance) != NULL;
|
||||
}
|
||||
|
||||
void Animator::onAncestorChanged(const AncestorChanged& event)
|
||||
{
|
||||
Super::onAncestorChanged(event);
|
||||
}
|
||||
|
||||
void Animator::onEvent_DescendantAdded(shared_ptr<Instance> descendant)
|
||||
{
|
||||
IAnimatableJoint* joint = dynamic_cast<IAnimatableJoint*>(descendant.get());
|
||||
if (joint)
|
||||
{
|
||||
Instance* figure = getRootInstance();
|
||||
calcAnimatableJoints(figure);
|
||||
|
||||
if (animatableJoints.size() > 0)
|
||||
setupClumpChangedListener(figure);
|
||||
}
|
||||
}
|
||||
|
||||
void Animator::onEvent_DescendantRemoving(shared_ptr<Instance> descendant)
|
||||
{
|
||||
IAnimatableJoint* joint = dynamic_cast<IAnimatableJoint*>(descendant.get());
|
||||
if (joint)
|
||||
{
|
||||
calcAnimatableJoints(getRootInstance(), descendant);
|
||||
|
||||
if (animatableJoints.size() == 0)
|
||||
clumpChangedConnection.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
void Animator::onEvent_ClumpChanged(shared_ptr<Instance> instance)
|
||||
{
|
||||
AYAASSERT(animatableJoints.size() > 0);
|
||||
|
||||
PartInstance* part = Instance::fastDynamicCast<PartInstance>(instance.get());
|
||||
if (Primitive* primitive = part->getPartPrimitive())
|
||||
{
|
||||
if (Assembly* assembly = primitive->getAssembly())
|
||||
{
|
||||
// check for new root assembly part
|
||||
PartInstance* rootPart = PartInstance::fromAssembly(assembly);
|
||||
if ((rootPart != part) && rootPart->isDescendantOf(getRootInstance()))
|
||||
clumpChangedConnection =
|
||||
rootPart->onDemandWrite()->clumpChangedSignal.connect(boost::bind(&Animator::onEvent_ClumpChanged, this, _1));
|
||||
|
||||
assembly->setAnimationControlled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Animator::onEvent_AncestorModified()
|
||||
{
|
||||
descentdantAdded = NULL;
|
||||
descentdantRemoved = NULL;
|
||||
ancestorChanged = NULL;
|
||||
clumpChangedConnection = NULL;
|
||||
|
||||
Instance* figure = getRootInstance();
|
||||
|
||||
if (figure)
|
||||
{
|
||||
descentdantAdded = figure->onDemandWrite()->descendantAddedSignal.connect(boost::bind(&Animator::onEvent_DescendantAdded, this, _1));
|
||||
descentdantRemoved = figure->onDemandWrite()->descendantRemovingSignal.connect(boost::bind(&Animator::onEvent_DescendantRemoving, this, _1));
|
||||
ancestorChanged = figure->ancestryChangedSignal.connect(boost::bind(&Animator::onEvent_AncestorModified, this));
|
||||
}
|
||||
calcAnimatableJoints(figure);
|
||||
|
||||
// listen to assembly change only if we have animatable joints, so we can mark it as animation controlled
|
||||
if (animatableJoints.size() > 0)
|
||||
setupClumpChangedListener(figure);
|
||||
}
|
||||
|
||||
void Animator::onPlay(ContentId animation, float fadeTime, float weight, float speed)
|
||||
{
|
||||
std::map<ContentId, shared_ptr<AnimationTrack>>::iterator node = animationTrackMap.find(animation);
|
||||
if (node == animationTrackMap.end())
|
||||
{
|
||||
passiveLoadAnimation(animation);
|
||||
node = animationTrackMap.find(animation);
|
||||
}
|
||||
if (node != animationTrackMap.end())
|
||||
{
|
||||
if (Workspace::showActiveAnimationAsset)
|
||||
{
|
||||
StandardOut::singleton()->printf(MESSAGE_INFO, "Play animation: %s", node->first.c_str());
|
||||
}
|
||||
node->second->localPlay(fadeTime, weight, speed);
|
||||
if (Workspace::showActiveAnimationAsset)
|
||||
{
|
||||
activeAnimation = node->second->getAnimationName();
|
||||
activeAnimation.append(" (");
|
||||
activeAnimation.append(node->first.c_str());
|
||||
activeAnimation.append(")");
|
||||
}
|
||||
}
|
||||
|
||||
tellParentAnimationPlayed(node->second);
|
||||
}
|
||||
|
||||
shared_ptr<const Reflection::ValueArray> Animator::getPlayingAnimationTracks()
|
||||
{
|
||||
shared_ptr<Reflection::ValueArray> playingAnimationTracks(Aya::make_shared<Reflection::ValueArray>());
|
||||
std::list<shared_ptr<AnimationTrackState>>::iterator node;
|
||||
for (node = activeAnimations.begin(); node != activeAnimations.end(); ++node)
|
||||
{
|
||||
shared_ptr<Instance> animationTrack(node->get()->getAnimationTrack());
|
||||
playingAnimationTracks->push_back(animationTrack);
|
||||
}
|
||||
return playingAnimationTracks;
|
||||
}
|
||||
|
||||
void Animator::tellParentAnimationPlayed(shared_ptr<Instance> animationTrack)
|
||||
{
|
||||
Instance* parent = getParent();
|
||||
if (parent)
|
||||
{
|
||||
AnimationController* animationController = Instance::fastDynamicCast<AnimationController>(parent);
|
||||
if (animationController)
|
||||
{
|
||||
animationController->animationPlayedSignal(animationTrack);
|
||||
}
|
||||
else
|
||||
{
|
||||
Humanoid* humanoid = Instance::fastDynamicCast<Humanoid>(parent);
|
||||
if (humanoid)
|
||||
{
|
||||
humanoid->animationPlayedSignal(animationTrack);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Animator::onStop(ContentId animation, float fadeTime)
|
||||
{
|
||||
std::map<ContentId, shared_ptr<AnimationTrack>>::iterator node = animationTrackMap.find(animation);
|
||||
if (node != animationTrackMap.end())
|
||||
{
|
||||
if (Workspace::showActiveAnimationAsset)
|
||||
{
|
||||
StandardOut::singleton()->printf(MESSAGE_INFO, "Stop animation: %s", node->first.c_str());
|
||||
}
|
||||
node->second->localStop(fadeTime);
|
||||
}
|
||||
}
|
||||
|
||||
void Animator::onAdjustSpeed(ContentId animation, float speed)
|
||||
{
|
||||
std::map<ContentId, shared_ptr<AnimationTrack>>::iterator node = animationTrackMap.find(animation);
|
||||
if (node != animationTrackMap.end())
|
||||
{
|
||||
node->second->localAdjustSpeed(speed);
|
||||
}
|
||||
}
|
||||
|
||||
void Animator::onSetTimePosition(ContentId animation, float timePosition)
|
||||
{
|
||||
std::map<ContentId, shared_ptr<AnimationTrack>>::iterator node = animationTrackMap.find(animation);
|
||||
if (node != animationTrackMap.end())
|
||||
{
|
||||
node->second->localSetTimePosition(timePosition);
|
||||
}
|
||||
}
|
||||
|
||||
void Animator::passiveLoadAnimation(ContentId animationId)
|
||||
{
|
||||
if (animationTrackMap.find(animationId) == animationTrackMap.end())
|
||||
{
|
||||
if (Workspace::showActiveAnimationAsset)
|
||||
{
|
||||
StandardOut::singleton()->printf(MESSAGE_INFO, "Add animation: %s", animationId.toString().c_str());
|
||||
}
|
||||
shared_ptr<Animation> sharedAnimation(Creatable<Instance>::create<Animation>());
|
||||
sharedAnimation->setAssetId(animationId);
|
||||
shared_ptr<Instance> animationTrackInstance = loadAnimation(sharedAnimation);
|
||||
if (shared_ptr<AnimationTrack> animationTrack = boost::dynamic_pointer_cast<AnimationTrack>(animationTrackInstance))
|
||||
{
|
||||
animationTrackMap.insert(std::pair<ContentId, shared_ptr<AnimationTrack>>(animationId, animationTrack));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Animator::replicateAnimationPlay(ContentId animation, float fadeTime, float weight, float speed, shared_ptr<Instance> track)
|
||||
{
|
||||
// replicate the event
|
||||
event_OnPlay.replicateEvent(this, animation, fadeTime, weight, speed);
|
||||
|
||||
// call it locally with the track
|
||||
tellParentAnimationPlayed(track);
|
||||
}
|
||||
|
||||
void Animator::replicateAnimationStop(ContentId animation, float fadeTime)
|
||||
{
|
||||
event_OnStop.replicateEvent(this, animation, fadeTime);
|
||||
}
|
||||
|
||||
void Animator::replicateAnimationSpeed(ContentId animation, float speed)
|
||||
{
|
||||
event_OnAdjustSpeed.replicateEvent(this, animation, speed);
|
||||
}
|
||||
|
||||
void Animator::replicateAnimationTimePosition(ContentId animation, float timePosition)
|
||||
{
|
||||
event_OnSetTimePosition.replicateEvent(this, animation, timePosition);
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
103
engine/app/src/DataModel/Animator.hpp
Normal file
103
engine/app/src/DataModel/Animator.hpp
Normal file
@@ -0,0 +1,103 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Tree/Instance.hpp"
|
||||
#include "Utility/SteppedInstance.hpp"
|
||||
|
||||
#include "DataModel/KeyframeSequence.hpp" //for Animation::Priority, CachedPose
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class AnimatableRootJoint;
|
||||
class AnimationTrackState;
|
||||
class PartInstance;
|
||||
class AnimationTrack;
|
||||
|
||||
extern const char* const sAnimator;
|
||||
|
||||
class Animator
|
||||
: public DescribedCreatable<Animator, Instance, sAnimator>
|
||||
, public IStepped
|
||||
{
|
||||
private:
|
||||
typedef DescribedCreatable<Animator, Instance, sAnimator> Super;
|
||||
|
||||
Aya::Time serverLockTimer;
|
||||
|
||||
Instance* getRootInstance();
|
||||
void setupClumpChangedListener(Instance* rootInstance);
|
||||
|
||||
protected:
|
||||
std::map<ContentId, shared_ptr<AnimationTrack>> animationTrackMap;
|
||||
std::string activeAnimation;
|
||||
|
||||
typedef std::vector<JointPair> AnimatableJointSet;
|
||||
AnimatableJointSet animatableJoints;
|
||||
void calcAnimatableJoints(Instance* rootInstance, shared_ptr<Instance> descendant = shared_ptr<Instance>());
|
||||
void appendAnimatableJointsRec(shared_ptr<Instance> instance, shared_ptr<Instance> exclude);
|
||||
|
||||
scoped_ptr<AnimatableRootJoint> animatableRootJoint;
|
||||
|
||||
Aya::signals::scoped_connection descentdantAdded;
|
||||
Aya::signals::scoped_connection descentdantRemoved;
|
||||
Aya::signals::scoped_connection ancestorChanged;
|
||||
Aya::signals::scoped_connection clumpChangedConnection;
|
||||
void onEvent_DescendantAdded(shared_ptr<Instance> descendant);
|
||||
void onEvent_DescendantRemoving(shared_ptr<Instance> descendant);
|
||||
void onEvent_AncestorModified();
|
||||
void onEvent_ClumpChanged(shared_ptr<Instance> instance);
|
||||
|
||||
std::list<shared_ptr<AnimationTrackState>> activeAnimations;
|
||||
|
||||
public:
|
||||
Animator();
|
||||
Animator(Instance* replicatingContainer); // use this to add Animator behavor to another Instance in the tree, like Humanoid.
|
||||
|
||||
virtual ~Animator();
|
||||
|
||||
float getGameTime() const;
|
||||
shared_ptr<Instance> loadAnimation(shared_ptr<Instance> animation);
|
||||
void reloadAnimation(shared_ptr<AnimationTrackState> animationTrackState);
|
||||
|
||||
shared_ptr<PartInstance> testForServerLockPart;
|
||||
|
||||
/*implement*/ void onStepped(const Stepped& event);
|
||||
|
||||
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
|
||||
|
||||
/*override*/ void verifySetParent(const Instance* instance) const;
|
||||
/*override*/ bool askSetParent(const Instance* instance) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
/*override*/ bool askAddChild(const Instance* instance) const;
|
||||
/*override*/ void onAncestorChanged(const AncestorChanged& event);
|
||||
|
||||
Aya::remote_signal<void(ContentId, float, float, float)> onPlaySignal;
|
||||
Aya::remote_signal<void(ContentId, float)> onStopSignal;
|
||||
Aya::remote_signal<void(ContentId, float)> onAdjustSpeedSignal;
|
||||
Aya::remote_signal<void(ContentId, float)> onSetTimePositionSignal;
|
||||
void onPlay(ContentId animation, float fadeTime, float weight, float speed);
|
||||
void onStop(ContentId animation, float fadeTime);
|
||||
void onAdjustSpeed(ContentId animation, float speed);
|
||||
void onSetTimePosition(ContentId animation, float timePosition);
|
||||
void passiveLoadAnimation(ContentId animation);
|
||||
void replicateAnimationPlay(ContentId animation, float fadeTime, float weight, float speed, shared_ptr<Instance> track);
|
||||
void replicateAnimationStop(ContentId animation, float fadeTime);
|
||||
void replicateAnimationSpeed(ContentId animation, float speed);
|
||||
void replicateAnimationTimePosition(ContentId animation, float timePosition);
|
||||
std::string getActiveAnimation() const
|
||||
{
|
||||
return activeAnimation;
|
||||
}
|
||||
|
||||
void tellParentAnimationPlayed(shared_ptr<Instance> animationTrack);
|
||||
shared_ptr<const Reflection::ValueArray> getPlayingAnimationTracks();
|
||||
|
||||
private:
|
||||
void onTrackStepped(
|
||||
shared_ptr<AnimationTrackState> trackinst, double time, KeyframeSequence::Priority priority, std::vector<PoseAccumulator>* poses);
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
198
engine/app/src/DataModel/ArcHandles.cpp
Normal file
198
engine/app/src/DataModel/ArcHandles.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
|
||||
|
||||
|
||||
#include "DataModel/ArcHandles.hpp"
|
||||
#include "World/Primitive.hpp"
|
||||
#include "DataModel/Workspace.hpp"
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
#include "DataModel/MouseCommand.hpp"
|
||||
#include "Players.hpp"
|
||||
#include "DrawAdorn.hpp"
|
||||
#include "Utility/HitTest.hpp"
|
||||
#include "Utility/Faces.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
const char* const sArcHandles = "ArcHandles";
|
||||
|
||||
|
||||
static const Reflection::PropDescriptor<ArcHandles, Axes> prop_Axes("Axes", category_Data, &ArcHandles::getAxes, &ArcHandles::setAxes);
|
||||
|
||||
Reflection::RemoteEventDesc<ArcHandles, void(Aya::Vector3::Axis)> event_MouseEnter(&ArcHandles::mouseEnterSignal, "MouseEnter", "axis",
|
||||
Security::None, Reflection::RemoteEventCommon::SCRIPTING, Reflection::RemoteEventCommon::CLIENT_SERVER);
|
||||
Reflection::RemoteEventDesc<ArcHandles, void(Aya::Vector3::Axis)> event_MouseLeave(&ArcHandles::mouseLeaveSignal, "MouseLeave", "axis",
|
||||
Security::None, Reflection::RemoteEventCommon::SCRIPTING, Reflection::RemoteEventCommon::CLIENT_SERVER);
|
||||
Reflection::RemoteEventDesc<ArcHandles, void(Aya::Vector3::Axis, float, float)> event_MouseDrag(&ArcHandles::mouseDragSignal, "MouseDrag", "axis",
|
||||
"relativeAngle", "deltaRadius", Security::None, Reflection::RemoteEventCommon::SCRIPTING, Reflection::RemoteEventCommon::CLIENT_SERVER);
|
||||
Reflection::RemoteEventDesc<ArcHandles, void(Aya::Vector3::Axis)> event_MouseButton1Down(&ArcHandles::mouseButton1DownSignal, "MouseButton1Down",
|
||||
"axis", Security::None, Reflection::RemoteEventCommon::SCRIPTING, Reflection::RemoteEventCommon::CLIENT_SERVER);
|
||||
Reflection::RemoteEventDesc<ArcHandles, void(Aya::Vector3::Axis)> event_MouseButton1Up(&ArcHandles::mouseButton1UpSignal, "MouseButton1Up", "axis",
|
||||
Security::None, Reflection::RemoteEventCommon::SCRIPTING, Reflection::RemoteEventCommon::CLIENT_SERVER);
|
||||
|
||||
IMPLEMENT_EVENT_REPLICATOR(ArcHandles, event_MouseEnter, "MouseEnter", MouseEnter);
|
||||
IMPLEMENT_EVENT_REPLICATOR(ArcHandles, event_MouseLeave, "MouseLeave", MouseLeave);
|
||||
IMPLEMENT_EVENT_REPLICATOR(ArcHandles, event_MouseDrag, "MouseDrag", MouseDrag);
|
||||
IMPLEMENT_EVENT_REPLICATOR(ArcHandles, event_MouseButton1Down, "MouseButton1Down", MouseButton1Down);
|
||||
IMPLEMENT_EVENT_REPLICATOR(ArcHandles, event_MouseButton1Up, "MouseButton1Up", MouseButton1Up);
|
||||
REFLECTION_END();
|
||||
|
||||
|
||||
ArcHandles::ArcHandles()
|
||||
: DescribedCreatable<ArcHandles, HandlesBase, sArcHandles>("ArcHandles")
|
||||
, axes(0x7)
|
||||
, CONSTRUCT_EVENT_REPLICATOR(ArcHandles, ArcHandles::mouseEnterSignal, event_MouseEnter, MouseEnter)
|
||||
, CONSTRUCT_EVENT_REPLICATOR(ArcHandles, ArcHandles::mouseLeaveSignal, event_MouseLeave, MouseLeave)
|
||||
, CONSTRUCT_EVENT_REPLICATOR(ArcHandles, ArcHandles::mouseDragSignal, event_MouseDrag, MouseDrag)
|
||||
, CONSTRUCT_EVENT_REPLICATOR(ArcHandles, ArcHandles::mouseButton1DownSignal, event_MouseButton1Down, MouseButton1Down)
|
||||
, CONSTRUCT_EVENT_REPLICATOR(ArcHandles, ArcHandles::mouseButton1UpSignal, event_MouseButton1Up, MouseButton1Up)
|
||||
{
|
||||
CONNECT_EVENT_REPLICATOR(MouseEnter);
|
||||
CONNECT_EVENT_REPLICATOR(MouseLeave);
|
||||
CONNECT_EVENT_REPLICATOR(MouseDrag);
|
||||
CONNECT_EVENT_REPLICATOR(MouseButton1Down);
|
||||
CONNECT_EVENT_REPLICATOR(MouseButton1Up);
|
||||
}
|
||||
|
||||
void ArcHandles::setAxes(Axes value)
|
||||
{
|
||||
if (axes != value)
|
||||
{
|
||||
axes = value;
|
||||
raisePropertyChanged(prop_Axes);
|
||||
}
|
||||
}
|
||||
int ArcHandles::getHandlesNormalIdMask() const
|
||||
{
|
||||
Faces faces;
|
||||
faces.setNormalId(Aya::NORM_X, axes.getAxisByNormalId(Aya::NORM_X));
|
||||
faces.setNormalId(Aya::NORM_Y, axes.getAxisByNormalId(Aya::NORM_Y));
|
||||
faces.setNormalId(Aya::NORM_Z, axes.getAxisByNormalId(Aya::NORM_Z));
|
||||
|
||||
faces.setNormalId(Aya::NORM_X_NEG, axes.getAxisByNormalId(Aya::NORM_X));
|
||||
faces.setNormalId(Aya::NORM_Y_NEG, axes.getAxisByNormalId(Aya::NORM_Y));
|
||||
faces.setNormalId(Aya::NORM_Z_NEG, axes.getAxisByNormalId(Aya::NORM_Z));
|
||||
|
||||
return faces.normalIdMask;
|
||||
}
|
||||
|
||||
void ArcHandles::setServerGuiObject()
|
||||
{
|
||||
Super::setServerGuiObject();
|
||||
|
||||
// Since we are running the server, we need to be watching for local listeners
|
||||
eventReplicatorMouseEnter.setListenerMode(!mouseEnterSignal.empty());
|
||||
eventReplicatorMouseLeave.setListenerMode(!mouseLeaveSignal.empty());
|
||||
eventReplicatorMouseDrag.setListenerMode(!mouseDragSignal.empty());
|
||||
eventReplicatorMouseButton1Down.setListenerMode(!mouseButton1DownSignal.empty());
|
||||
eventReplicatorMouseButton1Up.setListenerMode(!mouseButton1UpSignal.empty());
|
||||
}
|
||||
|
||||
|
||||
void ArcHandles::onPropertyChanged(const Reflection::PropertyDescriptor& descriptor)
|
||||
{
|
||||
Super::onPropertyChanged(descriptor);
|
||||
|
||||
eventReplicatorMouseEnter.onPropertyChanged(descriptor);
|
||||
eventReplicatorMouseLeave.onPropertyChanged(descriptor);
|
||||
eventReplicatorMouseDrag.onPropertyChanged(descriptor);
|
||||
eventReplicatorMouseButton1Down.onPropertyChanged(descriptor);
|
||||
eventReplicatorMouseButton1Up.onPropertyChanged(descriptor);
|
||||
}
|
||||
|
||||
GuiResponse ArcHandles::process(const shared_ptr<InputObject>& event)
|
||||
{
|
||||
if (event->isMouseEvent())
|
||||
{
|
||||
// ignore mouse if not visible
|
||||
if (!getVisible())
|
||||
return GuiResponse::notSunk();
|
||||
|
||||
switch (event->getUserInputType())
|
||||
{
|
||||
case InputObject::TYPE_MOUSEMOVEMENT:
|
||||
{
|
||||
if (mouseDownCaptureInfo)
|
||||
{
|
||||
float relangle;
|
||||
float relradius;
|
||||
float absangle;
|
||||
float absradius;
|
||||
if (getAngleRadiusFromHandle(
|
||||
event, mouseDownCaptureInfo->hitNormalId, mouseDownCaptureInfo->hitPointWorld, relangle, relradius, absangle, absradius))
|
||||
{
|
||||
bool reverse = mouseDownCaptureInfo->hitNormalId >= NORM_X_NEG;
|
||||
|
||||
mouseDragSignal(Axes::normalIdToAxis(mouseDownCaptureInfo->hitNormalId), reverse ? -relangle : relangle, relradius);
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 hitPointWorld;
|
||||
NormalId hitNormalId;
|
||||
if (findTargetHandle(event, hitPointWorld, hitNormalId))
|
||||
{
|
||||
if (mouseOver != NORM_UNDEFINED && mouseOver != hitNormalId)
|
||||
{
|
||||
mouseLeaveSignal(Axes::normalIdToAxis(mouseOver));
|
||||
}
|
||||
|
||||
mouseOver = hitNormalId;
|
||||
mouseEnterSignal(Axes::normalIdToAxis(hitNormalId));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mouseOver != NORM_UNDEFINED)
|
||||
{
|
||||
mouseLeaveSignal(Axes::normalIdToAxis(hitNormalId));
|
||||
mouseOver = NORM_UNDEFINED;
|
||||
}
|
||||
}
|
||||
return GuiResponse::notSunk();
|
||||
}
|
||||
case InputObject::TYPE_MOUSEBUTTON1:
|
||||
{
|
||||
AYAASSERT(event->isLeftMouseDownEvent() || event->isLeftMouseUpEvent());
|
||||
|
||||
if (event->isLeftMouseDownEvent())
|
||||
{
|
||||
Vector3 hitPointWorld;
|
||||
NormalId hitNormalId;
|
||||
if (findTargetHandle(event, hitPointWorld, hitNormalId))
|
||||
{
|
||||
// Capture the MouseDown information for later use
|
||||
mouseDownCaptureInfo.reset(new MouseDownCaptureInfo(adornee.lock()->getLocation(), hitPointWorld, hitNormalId));
|
||||
|
||||
mouseButton1DownSignal(Axes::normalIdToAxis(hitNormalId));
|
||||
|
||||
return GuiResponse::sunk();
|
||||
}
|
||||
return GuiResponse::notSunk();
|
||||
}
|
||||
else if (event->isLeftMouseUpEvent())
|
||||
{
|
||||
// Clear our mouse down capture info
|
||||
mouseDownCaptureInfo.reset();
|
||||
|
||||
Vector3 hitPointWorld;
|
||||
NormalId hitNormalId;
|
||||
if (findTargetHandle(event, hitPointWorld, hitNormalId))
|
||||
{
|
||||
mouseButton1UpSignal(Axes::normalIdToAxis(hitNormalId));
|
||||
}
|
||||
return GuiResponse::notSunk();
|
||||
}
|
||||
return GuiResponse::notSunk();
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return GuiResponse::notSunk();
|
||||
}
|
||||
|
||||
Aya::HandleType ArcHandles::getHandleType() const
|
||||
{
|
||||
return HANDLE_ROTATE;
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
69
engine/app/src/DataModel/ArcHandles.hpp
Normal file
69
engine/app/src/DataModel/ArcHandles.hpp
Normal file
@@ -0,0 +1,69 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DataModel/HandlesBase.hpp"
|
||||
#include "DataModel/EventReplicator.hpp"
|
||||
#include "Base/IAdornable.hpp"
|
||||
#include "Utility/Axes.hpp"
|
||||
|
||||
#include "HandleType.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
extern const char* const sArcHandles;
|
||||
|
||||
class ArcHandles : public DescribedCreatable<ArcHandles, HandlesBase, sArcHandles>
|
||||
{
|
||||
private:
|
||||
typedef DescribedCreatable<ArcHandles, HandlesBase, sArcHandles> Super;
|
||||
|
||||
public:
|
||||
ArcHandles();
|
||||
|
||||
Aya::remote_signal<void(Aya::Vector3::Axis)> mouseEnterSignal;
|
||||
Aya::remote_signal<void(Aya::Vector3::Axis)> mouseLeaveSignal;
|
||||
Aya::remote_signal<void(Aya::Vector3::Axis, float, float)> mouseDragSignal;
|
||||
Aya::remote_signal<void(Aya::Vector3::Axis)> mouseButton1DownSignal;
|
||||
Aya::remote_signal<void(Aya::Vector3::Axis)> mouseButton1UpSignal;
|
||||
|
||||
DECLARE_EVENT_REPLICATOR_SIG(ArcHandles, MouseEnter, void(Aya::Vector3::Axis));
|
||||
DECLARE_EVENT_REPLICATOR_SIG(ArcHandles, MouseLeave, void(Aya::Vector3::Axis));
|
||||
DECLARE_EVENT_REPLICATOR_SIG(ArcHandles, MouseDrag, void(Aya::Vector3::Axis, float, float));
|
||||
DECLARE_EVENT_REPLICATOR_SIG(ArcHandles, MouseButton1Down, void(Aya::Vector3::Axis));
|
||||
DECLARE_EVENT_REPLICATOR_SIG(ArcHandles, MouseButton1Up, void(Aya::Vector3::Axis));
|
||||
|
||||
void setAxes(Axes value);
|
||||
Axes getAxes() const
|
||||
{
|
||||
return axes;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Instance
|
||||
/*override*/ void onPropertyChanged(const Reflection::PropertyDescriptor& descriptor);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// GuiBase
|
||||
/*override*/ GuiResponse process(const shared_ptr<InputObject>& event);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// HandlesBase
|
||||
/*override*/ Aya::HandleType getHandleType() const;
|
||||
|
||||
protected:
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// HandlesBase
|
||||
/*override*/ int getHandlesNormalIdMask() const;
|
||||
/*override*/ void setServerGuiObject();
|
||||
|
||||
private:
|
||||
Axes axes;
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
517
engine/app/src/DataModel/AssetService.cpp
Normal file
517
engine/app/src/DataModel/AssetService.cpp
Normal file
@@ -0,0 +1,517 @@
|
||||
|
||||
|
||||
#include "DataModel/AssetService.hpp"
|
||||
#include "DataModel/ContentProvider.hpp"
|
||||
#include "DataModel/MarketplaceService.hpp"
|
||||
#include "DataModel/HttpRbxApiService.hpp"
|
||||
#include "Players.hpp"
|
||||
#include "Utility/StandardOut.hpp"
|
||||
#include "Utility/LuaWebService.hpp"
|
||||
|
||||
#include "Xml/WebParser.hpp"
|
||||
#include "Utility/Http.hpp"
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
|
||||
DYNAMIC_FASTINTVARIABLE(CreatePlacePerMinute, 5)
|
||||
DYNAMIC_FASTINTVARIABLE(CreatePlacePerPlayerPerMinute, 1)
|
||||
|
||||
DYNAMIC_FASTINTVARIABLE(SavePlacePerMinute, 10)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
const char* const sAssetService = "AssetService";
|
||||
|
||||
|
||||
static Reflection::BoundFuncDesc<AssetService, void(std::string)> func_SetPlaceAccessUrl(
|
||||
&AssetService::setPlaceAccessUrl, "SetPlaceAccessUrl", "accessUrl", Security::LocalUser);
|
||||
static Reflection::BoundFuncDesc<AssetService, void(std::string)> func_SetAssetRevertUrl(
|
||||
&AssetService::setAssetRevertUrl, "SetAssetRevertUrl", "revertUrl", Security::LocalUser);
|
||||
static Reflection::BoundFuncDesc<AssetService, void(std::string)> func_setAssetVersionsUrl(
|
||||
&AssetService::setAssetVersionsUrl, "SetAssetVersionsUrl", "versionsUrl", Security::LocalUser);
|
||||
|
||||
static Reflection::BoundYieldFuncDesc<AssetService, bool(int, int)> func_revertAsset(
|
||||
&AssetService::revertAsset, "RevertAsset", "placeId", "versionNumber", Security::None);
|
||||
static Reflection::BoundYieldFuncDesc<AssetService, bool(int, AssetService::AccessType, shared_ptr<const Reflection::ValueArray>)>
|
||||
func_updatePermissions(&AssetService::setPlacePermissions, "SetPlacePermissions", "placeId", "accessType", AssetService::EVERYONE, "inviteList",
|
||||
shared_ptr<const Reflection::ValueArray>(), Security::None);
|
||||
static Reflection::BoundYieldFuncDesc<AssetService, shared_ptr<const Reflection::ValueTable>(int)> func_getPlacePermissions(
|
||||
&AssetService::getPlacePermissions, "GetPlacePermissions", "placeId", Security::None);
|
||||
static Reflection::BoundYieldFuncDesc<AssetService, shared_ptr<const Reflection::ValueTable>(int, int)> func_getAssetVersions(
|
||||
&AssetService::getAssetVersions, "GetAssetVersions", "placeId", "pageNum", 1, Security::None);
|
||||
|
||||
static Reflection::BoundYieldFuncDesc<AssetService, int(int)> func_getCreatorAssetID(
|
||||
&AssetService::getCreatorAssetID, "GetCreatorAssetID", "creationID", Security::None);
|
||||
|
||||
static Reflection::BoundYieldFuncDesc<AssetService, int(std::string, int, std::string)> func_createPlace(
|
||||
&AssetService::createPlaceAsync, "CreatePlaceAsync", "placeName", "templatePlaceID", "description", "", Security::None);
|
||||
|
||||
static Reflection::BoundYieldFuncDesc<AssetService, int(shared_ptr<Instance>, std::string, int, std::string)> func_createPlaceInPlayerInventory(
|
||||
&AssetService::createPlaceInPlayerInventoryAsync, "CreatePlaceInPlayerInventoryAsync", "player", "placeName", "templatePlaceID", "description",
|
||||
"", Security::None);
|
||||
static Reflection::BoundYieldFuncDesc<AssetService, void()> func_savePlace(&AssetService::savePlaceAsync, "SavePlaceAsync", Security::None);
|
||||
|
||||
static Reflection::BoundYieldFuncDesc<AssetService, shared_ptr<Instance>()> func_getGamePlacesAsync(
|
||||
&AssetService::getGamePlacesAsync, "GetGamePlacesAsync", Security::None);
|
||||
REFLECTION_END();
|
||||
|
||||
namespace Reflection
|
||||
{
|
||||
template<>
|
||||
EnumDesc<AssetService::AccessType>::EnumDesc()
|
||||
: EnumDescriptor("AccessType")
|
||||
{
|
||||
addPair(AssetService::ME, "Me");
|
||||
addPair(AssetService::FRIENDS, "Friends");
|
||||
addPair(AssetService::EVERYONE, "Everyone");
|
||||
addPair(AssetService::INVITEONLY, "InviteOnly");
|
||||
}
|
||||
|
||||
template<>
|
||||
AssetService::AccessType& Variant::convert<AssetService::AccessType>(void)
|
||||
{
|
||||
return genericConvert<AssetService::AccessType>();
|
||||
}
|
||||
} // namespace Reflection
|
||||
|
||||
template<>
|
||||
bool StringConverter<AssetService::AccessType>::convertToValue(const std::string& text, AssetService::AccessType& value)
|
||||
{
|
||||
return Reflection::EnumDesc<AssetService::AccessType>::singleton().convertToValue(text.c_str(), value);
|
||||
}
|
||||
|
||||
|
||||
AssetService::AssetService()
|
||||
: placeAccessUrl("")
|
||||
, assetRevertUrl("")
|
||||
, assetVersionsUrl("")
|
||||
, createPlaceThrottle(&DFInt::CreatePlacePerMinute, &DFInt::CreatePlacePerPlayerPerMinute)
|
||||
, savePlaceThrottle(&DFInt::SavePlacePerMinute)
|
||||
{
|
||||
setName(sAssetService);
|
||||
}
|
||||
|
||||
|
||||
void AssetService::setPlaceAccessUrl(std::string url)
|
||||
{
|
||||
placeAccessUrl = url;
|
||||
}
|
||||
|
||||
void AssetService::setAssetRevertUrl(std::string url)
|
||||
{
|
||||
assetRevertUrl = url;
|
||||
}
|
||||
|
||||
void AssetService::setAssetVersionsUrl(std::string url)
|
||||
{
|
||||
assetVersionsUrl = url;
|
||||
}
|
||||
|
||||
|
||||
std::string enumToString(AssetService::AccessType eType)
|
||||
{
|
||||
switch (eType)
|
||||
{
|
||||
case AssetService::ME:
|
||||
return std::string("Me");
|
||||
case AssetService::FRIENDS:
|
||||
return std::string("Friends");
|
||||
case AssetService::EVERYONE:
|
||||
return std::string("Everyone");
|
||||
case AssetService::INVITEONLY:
|
||||
return std::string("InviteOnly");
|
||||
default:
|
||||
return std::string("Me");
|
||||
}
|
||||
}
|
||||
|
||||
void AssetService::processServiceResults(std::string* result, const std::exception* httpException,
|
||||
boost::function<void(shared_ptr<const Reflection::ValueTable>)> resumeFunction, boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
if (!httpException)
|
||||
{
|
||||
if (!result || result->empty())
|
||||
{
|
||||
errorFunction("Invalid response received");
|
||||
}
|
||||
shared_ptr<const Reflection::ValueTable> lTable;
|
||||
std::stringstream resultStream;
|
||||
resultStream << *result;
|
||||
|
||||
|
||||
if (WebParser::parseJSONTable(*result, lTable))
|
||||
resumeFunction(lTable);
|
||||
else
|
||||
errorFunction("AssetService error occurred");
|
||||
}
|
||||
else
|
||||
{
|
||||
errorFunction(Aya::format("Http exception occurred %s", httpException->what()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void CreatePlaceSuccessHelper(
|
||||
weak_ptr<DataModel> dm, const std::string& response, boost::function<void(int)> resumeFunction, boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
if (shared_ptr<DataModel> sharedDM = dm.lock())
|
||||
{
|
||||
if (!response.empty())
|
||||
{
|
||||
int newPlaceID = 0;
|
||||
try
|
||||
{
|
||||
newPlaceID = boost::lexical_cast<int>(response.c_str());
|
||||
}
|
||||
catch (boost::bad_lexical_cast const& e)
|
||||
{
|
||||
errorFunction(Aya::format("Game:CreatePlace response was not a valid placeId because %s", e.what()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPlaceID > 0)
|
||||
{
|
||||
resumeFunction(newPlaceID);
|
||||
}
|
||||
else
|
||||
{
|
||||
errorFunction("Game:CreatePlace response was not a valid placeId because id <= 0");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
errorFunction("Game:CreatePlace had no valid response");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void CreatePlaceErrorHelper(weak_ptr<DataModel> dm, const std::string& error, boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
if (shared_ptr<DataModel> sharedDM = dm.lock())
|
||||
{
|
||||
if (!error.empty())
|
||||
{
|
||||
errorFunction(Aya::format("Game:CreatePlace received and error: %s.", error.c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
errorFunction("Game:CreatePlace had no valid response in error helper.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AssetService::checkCreatePlaceAccess(const std::string& placeName, int templatePlaceId, std::string& message)
|
||||
{
|
||||
DataModel* dm = DataModel::get(this);
|
||||
AYAASSERT(dm);
|
||||
|
||||
if (!Network::Players::backendProcessing(this))
|
||||
{
|
||||
message = "CreatePlaceAsync can only be called from a server script, aborting create function";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (placeName.empty())
|
||||
{
|
||||
message = "CreatePlaceAsync placeName argument is empty!";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (templatePlaceId <= 0)
|
||||
{
|
||||
message = "CreatePlaceAsync templatePlaceId <= 0, should be a positive value";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dm->getPlaceID() <= 0)
|
||||
{
|
||||
message = "CreatePlaceAsync called on a Place with id <= 0, place should be opened with Edit button to access CreatePlace";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!createPlaceThrottle.checkLimit(Network::Players::getPlayerCount(this)))
|
||||
{
|
||||
message = "CreatePlaceAsync requests limit reached";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (LuaWebService* lws = dm->create<LuaWebService>())
|
||||
{
|
||||
if (!lws->isApiAccessEnabled())
|
||||
{
|
||||
message = "Studio API access is not enabled. Enable it by going to the Game Settings page.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AssetService::createPlaceAsync(std::string placeName, int templatePlaceId, std::string desc, boost::function<void(int)> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
std::string message;
|
||||
if (!checkCreatePlaceAccess(placeName, templatePlaceId, message))
|
||||
{
|
||||
errorFunction(message);
|
||||
return;
|
||||
}
|
||||
createPlaceAsyncInternal(true, placeName, templatePlaceId, desc, shared_ptr<Instance>(), resumeFunction, errorFunction);
|
||||
}
|
||||
|
||||
void AssetService::createPlaceInPlayerInventoryAsync(shared_ptr<Instance> player, std::string placeName, int templatePlaceId, std::string desc,
|
||||
boost::function<void(int)> resumeFunction, boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
std::string message;
|
||||
if (!checkCreatePlaceAccess(placeName, templatePlaceId, message))
|
||||
{
|
||||
errorFunction(message);
|
||||
return;
|
||||
}
|
||||
|
||||
MarketplaceService* marketPlaceService = ServiceProvider::create<MarketplaceService>(this);
|
||||
marketPlaceService->launchClientLuaDialog("Do you allow game to create new place in your inventory?", "Yes", "No", player,
|
||||
boost::bind(&AssetService::createPlaceAsyncInternal, this, _1, placeName, templatePlaceId, desc, _2, resumeFunction, errorFunction));
|
||||
}
|
||||
|
||||
void AssetService::createPlaceAsyncInternal(bool check, std::string placeName, int templatePlaceId, std::string desc,
|
||||
shared_ptr<Instance> playerInstance, boost::function<void(int)> resumeFunction, boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
if (!check)
|
||||
{
|
||||
StandardOut::singleton()->print(MESSAGE_WARNING, "User didn't allow place creation");
|
||||
resumeFunction(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
shared_ptr<Network::Player> player = fastSharedDynamicCast<Network::Player>(playerInstance);
|
||||
if (player && player->getUserID() <= 0)
|
||||
{
|
||||
errorFunction("CreatePlaceAsync requires player to be logged in");
|
||||
return;
|
||||
}
|
||||
|
||||
if (player)
|
||||
{
|
||||
Network::Players* players = ServiceProvider::find<Network::Players>(this);
|
||||
if (!players || players->getPlayerByID(player->getUserID()) != player)
|
||||
{
|
||||
errorFunction("Player no longer valid");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DataModel* dm = DataModel::get(this);
|
||||
|
||||
std::string parameters =
|
||||
Aya::format("?currentPlaceId=%d&placeName=%s&templatePlaceId=%d", dm->getPlaceID(), Aya::Http::urlEncode(placeName).c_str(), templatePlaceId);
|
||||
std::string baseUrl = ServiceProvider::create<ContentProvider>(this)->getApiBaseUrl();
|
||||
|
||||
std::string playerParameter = "";
|
||||
if (player)
|
||||
playerParameter = Aya::format("&playerId=%i", player->getUserID());
|
||||
|
||||
try
|
||||
{
|
||||
if (Aya::HttpRbxApiService* apiService = Aya::ServiceProvider::find<Aya::HttpRbxApiService>(this))
|
||||
{
|
||||
// now post to the website
|
||||
Http createHttp((baseUrl + "universes/new-place" + parameters + playerParameter).c_str());
|
||||
#ifndef _WIN32
|
||||
createHttp.setAuthDomain(baseUrl);
|
||||
#endif
|
||||
std::string postString("CreatePlacePost");
|
||||
apiService->postAsync(createHttp, postString, Aya::PRIORITY_SERVER_ELEVATED, HttpService::TEXT_PLAIN,
|
||||
boost::bind(&CreatePlaceSuccessHelper, shared_from(dm), _1, resumeFunction, errorFunction),
|
||||
boost::bind(&CreatePlaceErrorHelper, shared_from(dm), _1, errorFunction));
|
||||
}
|
||||
}
|
||||
catch (base_exception& e)
|
||||
{
|
||||
errorFunction(Aya::format("Game:CreatePlace failed because %s", e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void AssetService::savePlaceAsync(boost::function<void()> resumeFunction, boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
DataModel* dm = DataModel::get(this);
|
||||
dm->checkFetchExperimentalFeatures();
|
||||
|
||||
if (dm->getPlaceID() <= 0)
|
||||
{
|
||||
errorFunction("Game:SavePlace placeID is not valid!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!savePlaceThrottle.checkLimit())
|
||||
{
|
||||
errorFunction("Game:SavePlace requests limit reached");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Network::Players::frontendProcessing(this))
|
||||
{
|
||||
errorFunction("Game:SavePlace can only be called from a server script, aborting save function");
|
||||
return;
|
||||
}
|
||||
else if (Network::Players::backendProcessing(this))
|
||||
{
|
||||
if (LuaWebService* lws = dm->create<LuaWebService>())
|
||||
{
|
||||
if (!lws->isApiAccessEnabled())
|
||||
{
|
||||
errorFunction("Studio API access is not enabled. Enable it by going to the Game Settings page.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// construct the url
|
||||
std::string parameters = "?assetId=" + boost::lexical_cast<std::string>(dm->getPlaceID());
|
||||
parameters += "&isAppCreation=true";
|
||||
std::string baseUrl = ServiceProvider::create<ContentProvider>(this)->getBaseUrl();
|
||||
|
||||
// save the place
|
||||
dm->uploadPlace(baseUrl + "ide/publish/UploadExistingAsset" + parameters, SAVE_ALL, resumeFunction, errorFunction);
|
||||
}
|
||||
else
|
||||
{
|
||||
errorFunction("Game:SavePlace could not determine if client or server");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void AssetService::getAssetVersions(int assetId, int pageNum, boost::function<void(shared_ptr<const Reflection::ValueTable>)> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
Http http(Aya::format(assetVersionsUrl.c_str(), assetId, pageNum));
|
||||
http.get(boost::bind(&AssetService::processServiceResults, this, _1, _2, resumeFunction, errorFunction));
|
||||
}
|
||||
|
||||
void AssetService::getPlacePermissions(
|
||||
int placeId, boost::function<void(shared_ptr<const Reflection::ValueTable>)> resumeFunction, boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
Http http(Aya::format(placeAccessUrl.c_str(), placeId));
|
||||
http.get(boost::bind(&AssetService::processServiceResults, this, _1, _2, resumeFunction, errorFunction));
|
||||
}
|
||||
|
||||
void AssetService::httpPostHelper(std::string* response, std::exception* httpException, boost::function<void(bool)> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
if (!httpException)
|
||||
{
|
||||
if (!response)
|
||||
resumeFunction(false);
|
||||
if (*response == "")
|
||||
resumeFunction(false);
|
||||
else
|
||||
resumeFunction(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
errorFunction(httpException->what());
|
||||
}
|
||||
}
|
||||
|
||||
void AssetService::revertAsset(
|
||||
int assetId, int versionNumber, boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
Http http(Aya::format(assetRevertUrl.c_str(), assetId, versionNumber));
|
||||
std::string in;
|
||||
http.post(
|
||||
in, Http::kContentTypeDefaultUnspecified, true, boost::bind(&AssetService::httpPostHelper, this, _1, _2, resumeFunction, errorFunction));
|
||||
}
|
||||
|
||||
void AssetService::setPlacePermissions(int placeId, AccessType type, shared_ptr<const Reflection::ValueArray> inviteList,
|
||||
boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
std::string url = placeAccessUrl;
|
||||
url = Aya::format(url.append("/update?").c_str(), placeId);
|
||||
url = Aya::format(url.append("access=%s").c_str(), enumToString(type).c_str());
|
||||
|
||||
if (type == INVITEONLY && inviteList)
|
||||
{
|
||||
|
||||
for (Reflection::ValueArray::const_iterator iter = inviteList->begin(); iter != inviteList->end(); ++iter)
|
||||
{
|
||||
std::stringstream convert;
|
||||
convert << iter->get<std::string>();
|
||||
url.append("&players=").append(convert.str());
|
||||
}
|
||||
}
|
||||
Http http(url);
|
||||
std::string in;
|
||||
http.post(
|
||||
in, Http::kContentTypeDefaultUnspecified, true, boost::bind(&AssetService::httpPostHelper, this, _1, _2, resumeFunction, errorFunction));
|
||||
}
|
||||
|
||||
void AssetService::getCreatorAssetIDSuccessHelper(
|
||||
std::string response, boost::function<void(int)> resumeFunction, boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
if (response.length() == 0)
|
||||
{
|
||||
resumeFunction(0);
|
||||
return;
|
||||
}
|
||||
|
||||
int creatorID = 0;
|
||||
try
|
||||
{
|
||||
creatorID = boost::lexical_cast<int>(response);
|
||||
}
|
||||
catch (boost::bad_lexical_cast const& e)
|
||||
{
|
||||
errorFunction(Aya::format("AssetService:GetCreatorAssetID response could not convert to number because %s", e.what()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (creatorID <= 0)
|
||||
{
|
||||
errorFunction("AssetService:GetCreatorAssetID response converted but is an invalid assetID");
|
||||
return;
|
||||
}
|
||||
|
||||
resumeFunction(creatorID);
|
||||
}
|
||||
|
||||
void AssetService::getCreatorAssetIDErrorHelper(std::string error, boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
if (error.length() == 0)
|
||||
{
|
||||
errorFunction("AssetService::GetCreatorAssetID did not get a response or an error.");
|
||||
}
|
||||
|
||||
errorFunction(Aya::format("AssetService:GetCreatorAssetID error: %s", error.c_str()));
|
||||
}
|
||||
|
||||
void AssetService::getCreatorAssetID(int creationID, boost::function<void(int)> resumeFunction, boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
if (creationID <= 0)
|
||||
{
|
||||
errorFunction("creationID is not a valid number (should be a positive integer)");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string baseUrl = Aya::ServiceProvider::create<ContentProvider>(this)->getApiBaseUrl();
|
||||
std::string parameters = Aya::format("?creationID=%d", creationID);
|
||||
|
||||
if (Aya::HttpRbxApiService* apiService = Aya::ServiceProvider::find<Aya::HttpRbxApiService>(this))
|
||||
{
|
||||
apiService->getAsync("GetCreatorAssetID" + parameters, true, Aya::PRIORITY_DEFAULT,
|
||||
boost::bind(&AssetService::getCreatorAssetIDSuccessHelper, this, _1, resumeFunction, errorFunction),
|
||||
boost::bind(&AssetService::getCreatorAssetIDErrorHelper, this, _1, errorFunction));
|
||||
}
|
||||
}
|
||||
|
||||
void AssetService::getGamePlacesAsync(boost::function<void(shared_ptr<Instance>)> resumeFunction, boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
std::string fetchUrl;
|
||||
ContentProvider* cp = DataModel::get(this)->find<ContentProvider>();
|
||||
if (cp)
|
||||
fetchUrl = format("%suniverses/get-universe-places?placeid=%i", cp->getApiBaseUrl().c_str(), DataModel::get(this)->getPlaceID());
|
||||
|
||||
|
||||
shared_ptr<Pages> pagination = Creatable<Instance>::create<StandardPages>(weak_from(DataModel::get(this)), fetchUrl, "Places");
|
||||
pagination->fetchNextChunk(boost::bind(resumeFunction, pagination), errorFunction);
|
||||
}
|
||||
} // namespace Aya
|
||||
68
engine/app/src/DataModel/AssetService.hpp
Normal file
68
engine/app/src/DataModel/AssetService.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
#include "Tree/Instance.hpp"
|
||||
#include "Tree/Service.hpp"
|
||||
#include "RunningAverage.hpp"
|
||||
#include "DataModel.hpp"
|
||||
namespace Aya
|
||||
{
|
||||
extern const char* const sAssetService;
|
||||
class AssetService
|
||||
: public DescribedNonCreatable<AssetService, Instance, sAssetService>
|
||||
, public Service
|
||||
{
|
||||
ThrottlingHelper createPlaceThrottle, savePlaceThrottle;
|
||||
|
||||
public:
|
||||
enum AccessType
|
||||
{
|
||||
ME = 0,
|
||||
FRIENDS = 1,
|
||||
EVERYONE = 2,
|
||||
INVITEONLY = 3,
|
||||
};
|
||||
|
||||
AssetService();
|
||||
void setPlaceAccessUrl(std::string url);
|
||||
void setAssetRevertUrl(std::string url);
|
||||
void setAssetVersionsUrl(std::string url);
|
||||
|
||||
void revertAsset(int assetId, int versionNumber, boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
void getAssetVersions(int assetId, int pageNum, boost::function<void(shared_ptr<const Reflection::ValueTable>)> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction);
|
||||
void getPlacePermissions(int placeId, boost::function<void(shared_ptr<const Reflection::ValueTable>)> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction);
|
||||
void setPlacePermissions(int placeId, AccessType type, shared_ptr<const Reflection::ValueArray> inviteList,
|
||||
boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
|
||||
void getCreatorAssetID(int creationID, boost::function<void(int)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
|
||||
void createPlaceAsync(std::string placeName, int templatePlaceId, std::string desc, boost::function<void(int)> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction);
|
||||
|
||||
void createPlaceInPlayerInventoryAsync(shared_ptr<Instance> player, std::string placeName, int templatePlaceId, std::string desc,
|
||||
boost::function<void(int)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
|
||||
void savePlaceAsync(boost::function<void()> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
|
||||
void getGamePlacesAsync(boost::function<void(shared_ptr<Instance>)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
|
||||
|
||||
private:
|
||||
std::string placeAccessUrl;
|
||||
std::string assetRevertUrl;
|
||||
std::string assetVersionsUrl;
|
||||
void httpPostHelper(std::string* response, std::exception* httpException, boost::function<void(bool)> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction);
|
||||
void processServiceResults(std::string* result, const std::exception* httpException,
|
||||
boost::function<void(shared_ptr<const Reflection::ValueTable>)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
|
||||
void getCreatorAssetIDSuccessHelper(
|
||||
std::string response, boost::function<void(int)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
void getCreatorAssetIDErrorHelper(std::string error, boost::function<void(std::string)> errorFunction);
|
||||
|
||||
void createPlaceAsyncInternal(bool check, std::string placeName, int templatePlaceId, std::string desc, shared_ptr<Instance> player,
|
||||
boost::function<void(int)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
|
||||
bool checkCreatePlaceAccess(const std::string& placeName, int templatePlaceId, std::string& message);
|
||||
};
|
||||
} // namespace Aya
|
||||
432
engine/app/src/DataModel/Attachment.cpp
Normal file
432
engine/app/src/DataModel/Attachment.cpp
Normal file
@@ -0,0 +1,432 @@
|
||||
|
||||
|
||||
|
||||
#if 1 // disable until we are ready for new joint schema
|
||||
|
||||
#include "DataModel/Attachment.hpp"
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
#include "DataModel/Surface.hpp"
|
||||
#include "DataModel/Workspace.hpp"
|
||||
#include "Draw.hpp"
|
||||
#include "DrawAdorn.hpp"
|
||||
#include "Base/Adorn.hpp"
|
||||
#include "World/Primitive.hpp"
|
||||
#include "Utility/Color.hpp"
|
||||
|
||||
// #define ENABLE_AXES_API
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
using namespace Reflection;
|
||||
|
||||
const char* const sAttachment = "Attachment";
|
||||
|
||||
REFLECTION_BEGIN()
|
||||
// Streaming
|
||||
Reflection::PropDescriptor<Attachment, CoordinateFrame> Attachment::prop_Frame(
|
||||
"CFrame", category_Data, &Attachment::getFrameInPart, &Attachment::setFrameInPart, PropertyDescriptor::STANDARD);
|
||||
|
||||
// UI
|
||||
static const PropDescriptor<Attachment, Vector3> prop_Position(
|
||||
"Position", category_Data, &Attachment::getPivotInPart, &Attachment::setPivotInPart, PropertyDescriptor::UI);
|
||||
static const PropDescriptor<Attachment, Vector3> prop_Rotation(
|
||||
"Rotation", category_Data, &Attachment::getEulerAnglesInPart, &Attachment::setEulerAnglesInPart, PropertyDescriptor::UI);
|
||||
static const PropDescriptor<Attachment, Vector3> prop_WorldPosition(
|
||||
"WorldPosition", "Derived Data", &Attachment::getPivotInWorld, NULL, PropertyDescriptor::UI);
|
||||
static const PropDescriptor<Attachment, Vector3> prop_WorldRotation(
|
||||
"WorldRotation", "Derived Data", &Attachment::getEulerAnglesInWorld, NULL, PropertyDescriptor::UI);
|
||||
|
||||
#ifdef ENABLE_AXES_API
|
||||
static const PropDescriptor<Attachment, Vector3> prop_Axis("Axis", "Derived Data", &Attachment::getAxisInPart, NULL, PropertyDescriptor::UI);
|
||||
static const PropDescriptor<Attachment, Vector3> prop_SecondaryAxis(
|
||||
"SecondaryAxis", "Derived Data", &Attachment::getSecondaryAxisInPart, NULL, PropertyDescriptor::UI);
|
||||
static const PropDescriptor<Attachment, Vector3> prop_WorldAxis(
|
||||
"WorldAxis", "Derived Data", &Attachment::getAxisInWorld, NULL, PropertyDescriptor::UI);
|
||||
static const PropDescriptor<Attachment, Vector3> prop_WorldSecondaryAxis(
|
||||
"WorldSecondaryAxis", "Derived Data", &Attachment::getSecondaryAxisInWorld, NULL, PropertyDescriptor::UI);
|
||||
|
||||
static BoundFuncDesc<Attachment, void(Vector3, Vector3)> func_setAxes(&Attachment::setAxes, "SetAxes", "axis0", "axis1", Security::None);
|
||||
static BoundFuncDesc<Attachment, void(Vector3)> func_setAxis(&Attachment::setAxisInPart, "SetAxis", "axis", Security::None);
|
||||
#endif
|
||||
|
||||
#ifdef AYA_ATTACHMENT_LOCKING
|
||||
const PropDescriptor<Attachment, bool> Attachment::prop_Locked(
|
||||
"Locked", category_Behavior, &Attachment::getLocked, &Attachment::setLocked, PropertyDescriptor::UI);
|
||||
#endif
|
||||
|
||||
REFLECTION_END()
|
||||
|
||||
float Attachment::toolAdornHandleRadius = 0.2f;
|
||||
float Attachment::toolAdornMajorAxisSize = 0.4f;
|
||||
float Attachment::toolAdornMajorAxisRadius = 0.07f;
|
||||
float Attachment::toolAdornMinorAxisSize = 0.3f;
|
||||
float Attachment::toolAdornMinorAxisRadius = 0.04f;
|
||||
float Attachment::adornRadius = 0.2f;
|
||||
|
||||
Attachment::Attachment()
|
||||
: visible(false)
|
||||
, locked(false)
|
||||
, axisDirectionInPart(1, 0, 0)
|
||||
, secondaryAxisDirectionInPart(0, 1, 0)
|
||||
, pivotPositionInPart(1, 0, 0)
|
||||
{
|
||||
setName("Attachment");
|
||||
}
|
||||
|
||||
void Attachment::setVisible(bool value)
|
||||
{
|
||||
if (value != visible)
|
||||
{
|
||||
visible = value;
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 Attachment::getEulerAnglesInPart() const
|
||||
{
|
||||
Matrix3 o = getOrientationInPart();
|
||||
Vector3 r;
|
||||
o.toEulerAnglesXYZ(r.x, r.y, r.z);
|
||||
return Vector3(Math::radiansToDegrees(r.x), Math::radiansToDegrees(r.y), Math::radiansToDegrees(r.z));
|
||||
}
|
||||
|
||||
Vector3 Attachment::getEulerAnglesInWorld() const
|
||||
{
|
||||
Matrix3 o = getOrientationInWorld();
|
||||
Vector3 r;
|
||||
o.toEulerAnglesXYZ(r.x, r.y, r.z);
|
||||
return Vector3(Math::radiansToDegrees(r.x), Math::radiansToDegrees(r.y), Math::radiansToDegrees(r.z));
|
||||
}
|
||||
|
||||
void Attachment::setEulerAnglesInPart(const Vector3& v)
|
||||
{
|
||||
Matrix3 o = Matrix3::fromEulerAnglesXYZ(Math::degreesToRadians(v.x), Math::degreesToRadians(v.y), Math::degreesToRadians(v.z));
|
||||
setOrientationInPartInternal(o);
|
||||
raiseChanged(prop_Frame);
|
||||
}
|
||||
|
||||
void Attachment::setOrientationInPartInternal(const Matrix3& o)
|
||||
{
|
||||
setAxisInPartInternal(o.column(0));
|
||||
setSecondaryAxisInPartInternal(o.column(1));
|
||||
|
||||
raiseChanged(prop_Rotation);
|
||||
raiseChanged(prop_WorldRotation);
|
||||
}
|
||||
|
||||
Matrix3 Attachment::getOrientationInPart() const
|
||||
{
|
||||
Matrix3 o;
|
||||
o.setColumn(0, axisDirectionInPart);
|
||||
o.setColumn(1, secondaryAxisDirectionInPart);
|
||||
o.setColumn(2, o.column(0).cross(o.column(1)));
|
||||
return o;
|
||||
}
|
||||
|
||||
Matrix3 Attachment::getOrientationInWorld() const
|
||||
{
|
||||
Matrix3 o;
|
||||
CoordinateFrame partCF = getParentFrame();
|
||||
o.setColumn(0, partCF.vectorToWorldSpace(axisDirectionInPart));
|
||||
o.setColumn(1, partCF.vectorToWorldSpace(secondaryAxisDirectionInPart));
|
||||
o.setColumn(2, o.column(0).cross(o.column(1)));
|
||||
return o;
|
||||
}
|
||||
|
||||
void Attachment::setPivotInPart(const Vector3& pivot)
|
||||
{
|
||||
pivotPositionInPart = pivot;
|
||||
raiseChanged(prop_Position);
|
||||
raiseChanged(prop_WorldPosition);
|
||||
raiseChanged(prop_Frame);
|
||||
}
|
||||
|
||||
Vector3 Attachment::getPivotInPart() const
|
||||
{
|
||||
return pivotPositionInPart;
|
||||
}
|
||||
|
||||
Vector3 Attachment::getPivotInWorld(void) const
|
||||
{
|
||||
return getParentFrame().pointToWorldSpace(pivotPositionInPart);
|
||||
}
|
||||
|
||||
void Attachment::setAxisInPartInternal(const Vector3& _axis)
|
||||
{
|
||||
if (_axis.magnitude() < 0.0001f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
axisDirectionInPart = _axis;
|
||||
axisDirectionInPart.unitize();
|
||||
#ifdef ENABLE_AXES_API
|
||||
raiseChanged(prop_Axis);
|
||||
raiseChanged(prop_WorldAxis);
|
||||
#endif
|
||||
|
||||
// This will orthonormalize the secondary axis to the axis
|
||||
setSecondaryAxisInPartInternal(secondaryAxisDirectionInPart);
|
||||
}
|
||||
|
||||
void Attachment::setAxisInPart(Vector3 _axis)
|
||||
{
|
||||
setAxisInPartInternal(_axis);
|
||||
raiseChanged(prop_Rotation);
|
||||
raiseChanged(prop_WorldRotation);
|
||||
raiseChanged(prop_Frame);
|
||||
}
|
||||
|
||||
Vector3 Attachment::getAxisInPart(void) const
|
||||
{
|
||||
return axisDirectionInPart;
|
||||
}
|
||||
|
||||
Vector3 Attachment::getAxisInWorld(void) const
|
||||
{
|
||||
return getParentFrame().vectorToWorldSpace(axisDirectionInPart);
|
||||
}
|
||||
|
||||
Vector3 Attachment::getSecondaryAxisInWorld(void) const
|
||||
{
|
||||
return getParentFrame().vectorToWorldSpace(secondaryAxisDirectionInPart);
|
||||
}
|
||||
|
||||
Vector3 Attachment::getSecondaryAxisInPart(void) const
|
||||
{
|
||||
return secondaryAxisDirectionInPart;
|
||||
}
|
||||
|
||||
void Attachment::setSecondaryAxisInPartInternal(const Vector3& _axis2)
|
||||
{
|
||||
if (_axis2.magnitude() < 0.00001f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Vector3 axis2 = _axis2;
|
||||
|
||||
Vector3 axis;
|
||||
axis = axisDirectionInPart;
|
||||
|
||||
axis2.unitize();
|
||||
if (fabsf(axis2.dot(axis)) < 0.999f)
|
||||
{
|
||||
// The input secondary axis is not completely parallel to the axis, we can project
|
||||
axis2 = axis2 - axis2.dot(axis) * axis;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The secondary axis is parallel with the axis, we need to make a choice
|
||||
if (fabsf(axis.dot(Vector3::unitY())) < sqrtf(2.0f) * 0.5f)
|
||||
{
|
||||
// If the axis makes more than 45deg angle with Y axis, project the Y axis on the orthogonal plane of the axis
|
||||
Vector3 t = Vector3::unitY().cross(axis);
|
||||
axis2 = axis.cross(t);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fabsf(axis.x) + 0.1 > fabsf(axis.z))
|
||||
{
|
||||
Vector3 t = Vector3::unitZ().cross(axis);
|
||||
axis2 = axis.cross(t);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector3 t = Vector3::unitX().cross(axis);
|
||||
axis2 = axis.cross(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
axis2.unitize();
|
||||
AYAASSERT(fabsf(axis2.dot(axis)) < 0.00001f);
|
||||
|
||||
secondaryAxisDirectionInPart = axis2;
|
||||
#ifdef ENABLE_AXES_API
|
||||
raiseChanged(prop_SecondaryAxis);
|
||||
raiseChanged(prop_WorldSecondaryAxis);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Attachment::setAxes(Vector3 axis, Vector3 axis2)
|
||||
{
|
||||
setAxisInPartInternal(axis);
|
||||
setSecondaryAxisInPartInternal(axis2);
|
||||
raiseChanged(prop_Rotation);
|
||||
raiseChanged(prop_WorldRotation);
|
||||
raiseChanged(prop_Frame);
|
||||
}
|
||||
|
||||
CoordinateFrame Attachment::getFrameInPart() const
|
||||
{
|
||||
CoordinateFrame cf;
|
||||
cf.translation = getPivotInPart();
|
||||
cf.rotation = getOrientationInPart();
|
||||
return cf;
|
||||
}
|
||||
|
||||
void Attachment::setFrameInPart(const CoordinateFrame& frame)
|
||||
{
|
||||
setOrientationInPartInternal(frame.rotation);
|
||||
setPivotInPart(frame.translation);
|
||||
raiseChanged(prop_Frame);
|
||||
}
|
||||
|
||||
CoordinateFrame Attachment::getFrameInWorld() const
|
||||
{
|
||||
CoordinateFrame cf;
|
||||
cf.translation = getPivotInWorld();
|
||||
cf.rotation = getOrientationInWorld();
|
||||
return cf;
|
||||
}
|
||||
|
||||
CoordinateFrame Attachment::getParentFrame() const
|
||||
{
|
||||
if (!locked)
|
||||
{
|
||||
const PartInstance* part = Instance::fastDynamicCast<const PartInstance>(getParent());
|
||||
if (part)
|
||||
{
|
||||
return part->getCoordinateFrame();
|
||||
}
|
||||
}
|
||||
return CoordinateFrame();
|
||||
}
|
||||
|
||||
void Attachment::setLocked(bool value)
|
||||
{
|
||||
if (!locked && value)
|
||||
{
|
||||
Vector3 pivotInWorld = getPivotInWorld();
|
||||
Vector3 axisInWorld = getAxisInWorld();
|
||||
Vector3 secondaryAxisInWorld = getSecondaryAxisInWorld();
|
||||
|
||||
locked = true;
|
||||
pivotPositionInPart = pivotInWorld;
|
||||
axisDirectionInPart = axisInWorld;
|
||||
secondaryAxisDirectionInPart = secondaryAxisInWorld;
|
||||
#ifdef AYA_ATTACHMENT_LOCKING
|
||||
raiseChanged(prop_Locked);
|
||||
#endif
|
||||
raiseChanged(prop_Position);
|
||||
raiseChanged(prop_WorldPosition);
|
||||
raiseChanged(prop_Rotation);
|
||||
raiseChanged(prop_WorldRotation);
|
||||
raiseChanged(prop_Frame);
|
||||
}
|
||||
|
||||
if (locked && !value)
|
||||
{
|
||||
Vector3 pivotInPart = getPivotInPart();
|
||||
Vector3 axisInPart = getAxisInPart();
|
||||
Vector3 secondaryAxisInPart = getSecondaryAxisInPart();
|
||||
|
||||
locked = false;
|
||||
CoordinateFrame partFrame = getParentFrame();
|
||||
|
||||
pivotPositionInPart = partFrame.pointToObjectSpace(pivotInPart);
|
||||
axisDirectionInPart = partFrame.vectorToObjectSpace(axisInPart);
|
||||
secondaryAxisDirectionInPart = partFrame.vectorToObjectSpace(secondaryAxisInPart);
|
||||
#ifdef AYA_ATTACHMENT_LOCKING
|
||||
raiseChanged(prop_Locked);
|
||||
#endif
|
||||
raiseChanged(prop_Position);
|
||||
raiseChanged(prop_WorldPosition);
|
||||
raiseChanged(prop_Rotation);
|
||||
raiseChanged(prop_WorldRotation);
|
||||
raiseChanged(prop_Frame);
|
||||
}
|
||||
}
|
||||
|
||||
void Attachment::render3dAdorn(Adorn* adorn)
|
||||
{
|
||||
if (!visible)
|
||||
return;
|
||||
|
||||
adorn->setMaterial(Adorn::Material_Default);
|
||||
adorn->setObjectToWorldMatrix(getFrameInWorld());
|
||||
Sphere sphere;
|
||||
sphere.radius = adornRadius;
|
||||
adorn->sphere(sphere, Color3::green());
|
||||
}
|
||||
|
||||
static void renderToolAdornAxes(Adorn* adorn, const CoordinateFrame& cf)
|
||||
{
|
||||
{
|
||||
CoordinateFrame cf1 = cf;
|
||||
cf1.translation += 0.5f * Attachment::toolAdornMajorAxisSize * cf.rotation.column(0);
|
||||
DrawAdorn::cylinder(adorn, cf1, 0, Attachment::toolAdornMajorAxisSize, Attachment::toolAdornMajorAxisRadius, Color3::yellow());
|
||||
}
|
||||
|
||||
{
|
||||
CoordinateFrame cf1 = cf;
|
||||
cf1.translation += 0.5f * Attachment::toolAdornMinorAxisSize * cf.rotation.column(1);
|
||||
DrawAdorn::cylinder(adorn, cf1, 1, Attachment::toolAdornMinorAxisSize, Attachment::toolAdornMinorAxisRadius, Color3::red());
|
||||
}
|
||||
}
|
||||
|
||||
void Attachment::render3dToolAdorn(Adorn* adorn, Attachment::SelectState selectState)
|
||||
{
|
||||
float alpha = 1.0f;
|
||||
static float dimming = 0.7f;
|
||||
float intensity = dimming;
|
||||
Color3 color = (selectState & SelectState_Paired) ? Color3::green() : Color3::orange();
|
||||
adorn->setMaterial(Adorn::Material_SelfLit);
|
||||
|
||||
if (selectState & SelectState_Normal)
|
||||
{
|
||||
intensity = 1.0f;
|
||||
}
|
||||
|
||||
if (selectState & SelectState_Hovered)
|
||||
{
|
||||
intensity = 1.0f;
|
||||
adorn->setMaterial(Adorn::Material_SelfLitHighlight);
|
||||
}
|
||||
|
||||
if (selectState & SelectState_Hidden)
|
||||
{
|
||||
alpha = 0.1f;
|
||||
}
|
||||
|
||||
if ((selectState & SelectState_Normal) || (selectState & SelectState_Hovered))
|
||||
{
|
||||
renderToolAdornAxes(adorn, getFrameInWorld());
|
||||
}
|
||||
|
||||
adorn->setObjectToWorldMatrix(getFrameInWorld());
|
||||
Sphere sphere;
|
||||
sphere.radius = toolAdornHandleRadius;
|
||||
adorn->sphere(sphere, Color4(intensity * color, alpha));
|
||||
}
|
||||
|
||||
float Attachment::intersectAdornWithRay(const RbxRay& r)
|
||||
{
|
||||
Sphere s;
|
||||
s.center = getPivotInWorld();
|
||||
s.radius = toolAdornHandleRadius;
|
||||
return r.intersectionTime(s);
|
||||
}
|
||||
|
||||
void Attachment::verifySetParent(const Instance* instance) const
|
||||
{
|
||||
Super::verifySetParent(instance);
|
||||
|
||||
if (instance != NULL && instance->fastDynamicCast<PartInstance>() == NULL && instance->fastDynamicCast<Workspace>() == NULL)
|
||||
{
|
||||
throw Aya::runtime_error("Attachments can only be parented to parts or the workspace");
|
||||
}
|
||||
}
|
||||
|
||||
void Attachment::verifyAddChild(const Instance* newChild) const
|
||||
{
|
||||
Super::verifyAddChild(newChild);
|
||||
|
||||
/*&& newChild->fastDynamicCast< Constraints::Constraint >() == NULL*/
|
||||
/*if (newChild != NULL )
|
||||
{
|
||||
throw Aya::runtime_error("Attachments can't have children.");
|
||||
}*/
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
|
||||
#endif
|
||||
133
engine/app/src/DataModel/Attachment.hpp
Normal file
133
engine/app/src/DataModel/Attachment.hpp
Normal file
@@ -0,0 +1,133 @@
|
||||
|
||||
#if 1 // disable until we are ready for new joint schema
|
||||
#pragma once
|
||||
|
||||
#include "Tree/Instance.hpp"
|
||||
#include "Base/IAdornable.hpp"
|
||||
#include "Utility/NormalId.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
// This is not a streaming/replication safe option, do not enable!
|
||||
// #define AYA_ATTACHMENT_LOCKING
|
||||
|
||||
extern const char* const sAttachment;
|
||||
class Attachment
|
||||
: public DescribedCreatable<Attachment, Instance, sAttachment>
|
||||
, public IAdornable
|
||||
{
|
||||
public:
|
||||
// The adorn looks of the attachment
|
||||
static float adornRadius;
|
||||
|
||||
// The attachment adorn looks under the AttachmentTool
|
||||
static float toolAdornHandleRadius;
|
||||
static float toolAdornMajorAxisSize;
|
||||
static float toolAdornMajorAxisRadius;
|
||||
static float toolAdornMinorAxisSize;
|
||||
static float toolAdornMinorAxisRadius;
|
||||
|
||||
private:
|
||||
typedef DescribedCreatable<Attachment, Instance, sAttachment> Super;
|
||||
|
||||
// Should we render something in game
|
||||
bool visible;
|
||||
|
||||
bool locked;
|
||||
|
||||
Vector3 pivotPositionInPart;
|
||||
Vector3 axisDirectionInPart;
|
||||
Vector3 secondaryAxisDirectionInPart;
|
||||
|
||||
bool shouldRender3dAdorn() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
void verifySetParent(const Instance* instance) const override;
|
||||
void verifyAddChild(const Instance* newChild) const override;
|
||||
|
||||
public:
|
||||
static Reflection::PropDescriptor<Attachment, CoordinateFrame> prop_Frame;
|
||||
|
||||
Attachment();
|
||||
~Attachment() {}
|
||||
|
||||
// For UI, scripting and streaming
|
||||
bool getVisible(void) const
|
||||
{
|
||||
return visible;
|
||||
}
|
||||
void setVisible(bool value);
|
||||
|
||||
bool getLocked(void) const
|
||||
{
|
||||
return locked;
|
||||
}
|
||||
void setLocked(bool value);
|
||||
|
||||
// UI and replication
|
||||
CoordinateFrame getFrameInPart() const;
|
||||
void setFrameInPart(const CoordinateFrame& frame);
|
||||
CoordinateFrame getFrameInWorld() const;
|
||||
|
||||
// UI and scripting
|
||||
Vector3 getPivotInPart() const;
|
||||
void setPivotInPart(const Vector3& pivot);
|
||||
Vector3 getPivotInWorld(void) const;
|
||||
|
||||
Vector3 getEulerAnglesInPart() const;
|
||||
void setEulerAnglesInPart(const Vector3& v);
|
||||
Vector3 getEulerAnglesInWorld() const;
|
||||
|
||||
Vector3 getAxisInPart(void) const;
|
||||
Vector3 getAxisInWorld(void) const;
|
||||
|
||||
Vector3 getSecondaryAxisInPart(void) const;
|
||||
Vector3 getSecondaryAxisInWorld(void) const;
|
||||
|
||||
CoordinateFrame getParentFrame() const;
|
||||
|
||||
// Only for scripting
|
||||
void setAxes(Vector3 axis, Vector3 secondaryAxis);
|
||||
|
||||
// Hidden from API as it has side effects
|
||||
// Used only by AttachmentTool
|
||||
void setAxisInPart(Vector3 axis);
|
||||
|
||||
virtual float intersectAdornWithRay(const RbxRay& r);
|
||||
|
||||
#ifdef AYA_ATTACHMENT_LOCKING
|
||||
// disabled because not streaming/replication safe
|
||||
static const Reflection::PropDescriptor<Attachment, bool> prop_Locked;
|
||||
#endif
|
||||
|
||||
// Rendering
|
||||
enum SelectState
|
||||
{
|
||||
SelectState_None = 0,
|
||||
SelectState_Normal = 1,
|
||||
SelectState_Hovered = 2,
|
||||
SelectState_Paired = 4,
|
||||
SelectState_Hidden = 8
|
||||
};
|
||||
void render3dToolAdorn(Adorn* adorn, Attachment::SelectState selectState);
|
||||
void render3dAdorn(Adorn* adorn) override;
|
||||
|
||||
private:
|
||||
// Hidden from API for having order dependency issues
|
||||
|
||||
// This might change the secondary axis in order to keep it orthogonal to the axis
|
||||
void setAxisInPartInternal(const Vector3& axis);
|
||||
|
||||
// This will not change the axis, but will project the secondary axis onto the orthogonormal circle to the axis
|
||||
void setSecondaryAxisInPartInternal(const Vector3& axis);
|
||||
|
||||
void setOrientationInPartInternal(const Matrix3& o);
|
||||
Matrix3 getOrientationInPart() const;
|
||||
Matrix3 getOrientationInWorld() const;
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
|
||||
#endif
|
||||
59
engine/app/src/DataModel/Backpack.cpp
Normal file
59
engine/app/src/DataModel/Backpack.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
|
||||
|
||||
|
||||
#include "DataModel/Backpack.hpp"
|
||||
#include "DataModel/Hopper.hpp"
|
||||
#include "DataModel/Workspace.hpp"
|
||||
#include "Player.hpp"
|
||||
#include "Players.hpp"
|
||||
#include "Script/script.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
const char* const sBackpack = "Backpack"; // rename class ultimately
|
||||
|
||||
Backpack::Backpack()
|
||||
{
|
||||
setName("Backpack");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* When scripts run in the backpack
|
||||
|
||||
BaseScript: 1. In HopperBin if local backpack (runs local)
|
||||
2. If backend Processing (runs backend)
|
||||
|
||||
LocalScript:3. If local backpack (runs local)
|
||||
*/
|
||||
|
||||
bool Backpack::scriptShouldRun(BaseScript* script)
|
||||
{
|
||||
AYAASSERT(isAncestorOf(script));
|
||||
|
||||
Workspace* workspace = ServiceProvider::find<Workspace>(this);
|
||||
if (workspace)
|
||||
{
|
||||
Aya::Network::Player* localPlayer = Network::Players::findLocalPlayer(this);
|
||||
bool isLocalBackpack = (this->getParent() == localPlayer);
|
||||
bool isLocalScript = (script->fastDynamicCast<LocalScript>() != NULL);
|
||||
|
||||
if (isLocalBackpack && isLocalScript)
|
||||
{
|
||||
script->setLocalPlayer(shared_from(localPlayer));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isBackendProcessing = Network::Players::backendProcessing(this);
|
||||
|
||||
if (!isLocalScript && isBackendProcessing)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
} // namespace Aya
|
||||
25
engine/app/src/DataModel/Backpack.hpp
Normal file
25
engine/app/src/DataModel/Backpack.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DataModel/Hopper.hpp"
|
||||
#include "Script/IScriptFilter.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
extern const char* const sBackpack;
|
||||
class Backpack
|
||||
: public DescribedCreatable<Backpack, Hopper, sBackpack>
|
||||
, public IScriptFilter
|
||||
{
|
||||
private:
|
||||
// IScriptOwner
|
||||
/*override*/ bool scriptShouldRun(BaseScript* script);
|
||||
|
||||
public:
|
||||
Backpack();
|
||||
};
|
||||
|
||||
|
||||
} // namespace Aya
|
||||
450
engine/app/src/DataModel/BadgeService.cpp
Normal file
450
engine/app/src/DataModel/BadgeService.cpp
Normal file
@@ -0,0 +1,450 @@
|
||||
|
||||
|
||||
#include "DataModel/BadgeService.hpp"
|
||||
#include "DataModel/DebrisService.hpp"
|
||||
#include "DataModel/Workspace.hpp"
|
||||
#include "Players.hpp"
|
||||
#include "Utility/Http.hpp"
|
||||
#include "Utility/StandardOut.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
const char* const sBadgeService = "BadgeService";
|
||||
|
||||
|
||||
static Reflection::BoundYieldFuncDesc<BadgeService, bool(int, int)> func_UserHasBadge(
|
||||
&BadgeService::userHasBadge, "UserHasBadge", "userId", "badgeId", Security::None);
|
||||
static Reflection::BoundYieldFuncDesc<BadgeService, bool(int, int)> func_AwardBadge(
|
||||
&BadgeService::awardBadge, "AwardBadge", "userId", "badgeId", Security::None);
|
||||
static Reflection::BoundYieldFuncDesc<BadgeService, bool(int)> func_IsDisabled(&BadgeService::isDisabled, "IsDisabled", "badgeId", Security::None);
|
||||
static Reflection::BoundYieldFuncDesc<BadgeService, bool(int)> func_IsLegal(&BadgeService::isLegal, "IsLegal", "badgeId", Security::None);
|
||||
|
||||
static Reflection::BoundFuncDesc<BadgeService, void(int)> func_SetHasBadgeCooldown(
|
||||
&BadgeService::setHasBadgeCooldown, "SetHasBadgeCooldown", "seconds", Security::LocalUser);
|
||||
static Reflection::BoundFuncDesc<BadgeService, void(int)> func_SetPlaceId(&BadgeService::setPlaceId, "SetPlaceId", "placeId", Security::LocalUser);
|
||||
static Reflection::BoundFuncDesc<BadgeService, void(std::string)> func_SetAwardBadgeUrl(
|
||||
&BadgeService::setAwardBadgeUrl, "SetAwardBadgeUrl", "url", Security::LocalUser);
|
||||
static Reflection::BoundFuncDesc<BadgeService, void(std::string)> func_SetHasBadgeUrl(
|
||||
&BadgeService::setHasBadgeUrl, "SetHasBadgeUrl", "url", Security::LocalUser);
|
||||
static Reflection::BoundFuncDesc<BadgeService, void(std::string)> func_SetDisabledUrl(
|
||||
&BadgeService::setIsBadgeDisabledUrl, "SetIsBadgeDisabledUrl", "url", Security::LocalUser);
|
||||
static Reflection::BoundFuncDesc<BadgeService, void(std::string)> func_SetLegalUrl(
|
||||
&BadgeService::setIsBadgeLegalUrl, "SetIsBadgeLegalUrl", "url", Security::LocalUser);
|
||||
|
||||
static Reflection::RemoteEventDesc<BadgeService, void(std::string, int, int)> event_BadgeAwarded(&BadgeService::badgeAwardedSignal, "BadgeAwarded",
|
||||
"message", "userId", "badgeId", Security::RobloxScript, Reflection::RemoteEventCommon::SCRIPTING, Reflection::RemoteEventCommon::BROADCAST);
|
||||
REFLECTION_END();
|
||||
|
||||
BadgeService::BadgeService()
|
||||
: placeId(-1)
|
||||
, cooldownTime(60)
|
||||
{
|
||||
setName("BadgeService");
|
||||
}
|
||||
|
||||
void BadgeService::setPlaceId(int placeId)
|
||||
{
|
||||
this->placeId = placeId;
|
||||
}
|
||||
void BadgeService::setHasBadgeCooldown(int cooldownTime)
|
||||
{
|
||||
this->cooldownTime = cooldownTime;
|
||||
}
|
||||
|
||||
void BadgeService::setAwardBadgeUrl(std::string value)
|
||||
{
|
||||
awardBadgeUrl = value;
|
||||
}
|
||||
void BadgeService::setHasBadgeUrl(std::string value)
|
||||
{
|
||||
hasBadgeUrl = value;
|
||||
}
|
||||
void BadgeService::setIsBadgeDisabledUrl(std::string value)
|
||||
{
|
||||
isBadgeDisabledUrl = value;
|
||||
}
|
||||
void BadgeService::setIsBadgeLegalUrl(std::string value)
|
||||
{
|
||||
isBadgeLegalUrl = value;
|
||||
}
|
||||
|
||||
void BadgeService::hasBadgeResultHelper(weak_ptr<BadgeService> badgeService, int userId, int badgeId, std::string* response, std::exception* err,
|
||||
boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
if (shared_ptr<BadgeService> safeBadgeService = badgeService.lock())
|
||||
{
|
||||
safeBadgeService->hasBadgeResult(userId, badgeId, response, err, resumeFunction, errorFunction);
|
||||
}
|
||||
}
|
||||
void BadgeService::hasBadgeResult(int userId, int badgeId, std::string* response, std::exception* err, boost::function<void(bool)> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
if (!response)
|
||||
{
|
||||
resumeFunction(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (*response == "Success")
|
||||
{
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(badgeQuerySync);
|
||||
badgeQueryCache[userId].insert(badgeId);
|
||||
}
|
||||
resumeFunction(true);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(badgeQuerySync);
|
||||
hotBadges.push_back(HotUserHasBadge(userId, badgeId, cooldownTime));
|
||||
}
|
||||
if (*response == "Failure")
|
||||
{
|
||||
resumeFunction(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
resumeFunction(false);
|
||||
}
|
||||
}
|
||||
|
||||
BadgeService::HotUserHasBadge::HotUserHasBadge(int userId, int badgeId, int cooldownTime)
|
||||
: userId(userId)
|
||||
, badgeId(badgeId)
|
||||
, expiration(Aya::Time::now<Time::Fast>() + Time::Interval(cooldownTime))
|
||||
{
|
||||
}
|
||||
|
||||
bool BadgeService::HotUserHasBadge::expired() const
|
||||
{
|
||||
return Aya::Time::now<Time::Fast>() >= expiration;
|
||||
}
|
||||
|
||||
|
||||
bool BadgeService::isHasBadgeHot(int userId, int badgeId)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(badgeQuerySync);
|
||||
std::list<HotUserHasBadge>::iterator end = hotBadges.end();
|
||||
for (std::list<HotUserHasBadge>::iterator iter = hotBadges.begin(); iter != end; ++iter)
|
||||
{
|
||||
if (iter->expired())
|
||||
{
|
||||
hotBadges.erase(iter, end);
|
||||
return false;
|
||||
}
|
||||
if (iter->userId == userId && iter->badgeId == badgeId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void BadgeService::userHasBadge(int userId, int badgeId, boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
if (!Workspace::serverIsPresent(this))
|
||||
{
|
||||
// Throw a friendly exception to the users that they can't get badges during user games
|
||||
StandardOut::singleton()->printf(MESSAGE_WARNING, "Sorry, badges can only be queried by the Kiseki game servers");
|
||||
resumeFunction(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ServiceProvider::find<Network::Players>(this)->getPlayerByID(userId).get() == NULL)
|
||||
{
|
||||
StandardOut::singleton()->printf(MESSAGE_WARNING, "Sorry, player with userId=%d is not in this level at the moment", userId);
|
||||
resumeFunction(false);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
bool returnResult;
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(badgeQuerySync);
|
||||
returnResult = badgeQueryCache[userId].find(badgeId) != badgeQueryCache[userId].end();
|
||||
}
|
||||
|
||||
if (returnResult)
|
||||
{
|
||||
// They already have the badge, so don't bother asking the webserver
|
||||
resumeFunction(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isHasBadgeHot(userId, badgeId))
|
||||
{
|
||||
resumeFunction(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Make the request and send it out
|
||||
Http http(Aya::format(hasBadgeUrl.c_str(), userId, badgeId));
|
||||
http.get(boost::bind(&BadgeService::hasBadgeResultHelper, weak_from(this), userId, badgeId, _1, _2, resumeFunction, errorFunction));
|
||||
}
|
||||
|
||||
|
||||
void BadgeService::isDiabledResultHelper(weak_ptr<BadgeService> badgeService, int badgeId, std::string* response, std::exception* err,
|
||||
boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
if (shared_ptr<BadgeService> safeBadgeService = badgeService.lock())
|
||||
{
|
||||
safeBadgeService->isDisabledResult(badgeId, response, err, resumeFunction, errorFunction);
|
||||
}
|
||||
}
|
||||
void BadgeService::isDisabledResult(int badgeId, std::string* response, std::exception* err, boost::function<void(bool)> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
if (!response)
|
||||
{
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(badgeIsDisabledSync);
|
||||
badgeIsDisabledCache[badgeId] = true;
|
||||
}
|
||||
resumeFunction(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (*response == "1")
|
||||
{
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(badgeIsDisabledSync);
|
||||
badgeIsDisabledCache[badgeId] = true;
|
||||
}
|
||||
resumeFunction(true);
|
||||
}
|
||||
else if (*response == "0")
|
||||
{
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(badgeIsDisabledSync);
|
||||
badgeIsDisabledCache[badgeId] = false;
|
||||
}
|
||||
resumeFunction(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Really bad failure?
|
||||
resumeFunction(true);
|
||||
}
|
||||
}
|
||||
|
||||
void BadgeService::isDisabled(int badgeId, boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
if (!Workspace::serverIsPresent(this))
|
||||
{
|
||||
// Throw a friendly exception to the users that they can't get badges during user games
|
||||
StandardOut::singleton()->printf(MESSAGE_WARNING, "Sorry, badges can only be tested if they are disabled on Kiseki game servers");
|
||||
resumeFunction(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (placeId == -1)
|
||||
{
|
||||
resumeFunction(true);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
bool hasResult;
|
||||
bool result;
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(badgeIsDisabledSync);
|
||||
// Check if we have already awarded the badge to this user in this game session
|
||||
hasResult = badgeIsDisabledCache.find(badgeId) != badgeIsDisabledCache.end();
|
||||
result = hasResult ? badgeIsDisabledCache[badgeId] : false;
|
||||
}
|
||||
if (hasResult)
|
||||
{
|
||||
// We've already awarded this badge during this game session (or the badge is no good), so you can't get it again.
|
||||
resumeFunction(result);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Make the request and send it out
|
||||
Http http(Aya::format(isBadgeDisabledUrl.c_str(), badgeId, placeId));
|
||||
http.get(boost::bind(&BadgeService::isDiabledResultHelper, weak_from(this), badgeId, _1, _2, resumeFunction, errorFunction));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void BadgeService::isLegalResultHelper(weak_ptr<BadgeService> badgeService, int badgeId, std::string* response, std::exception* err,
|
||||
boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
if (shared_ptr<BadgeService> safeBadgeService = badgeService.lock())
|
||||
{
|
||||
safeBadgeService->isLegalResult(badgeId, response, err, resumeFunction, errorFunction);
|
||||
}
|
||||
}
|
||||
void BadgeService::isLegalResult(int badgeId, std::string* response, std::exception* err, boost::function<void(bool)> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
if (!response)
|
||||
{
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(badgeIsLegalSync);
|
||||
badgeIsLegalCache[badgeId] = true;
|
||||
}
|
||||
resumeFunction(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (*response == "1")
|
||||
{
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(badgeIsLegalSync);
|
||||
badgeIsLegalCache[badgeId] = true;
|
||||
}
|
||||
resumeFunction(true);
|
||||
}
|
||||
else if (*response == "0")
|
||||
{
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(badgeIsLegalSync);
|
||||
badgeIsLegalCache[badgeId] = false;
|
||||
}
|
||||
resumeFunction(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Really bad failure?
|
||||
resumeFunction(true);
|
||||
}
|
||||
}
|
||||
|
||||
void BadgeService::isLegal(int badgeId, boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
if (placeId == -1)
|
||||
{
|
||||
// We don't know the placeId, so give up and assume its okay
|
||||
resumeFunction(true);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
bool hasResult;
|
||||
bool result;
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(badgeIsLegalSync);
|
||||
// Check if we have already awarded the badge to this user in this game session
|
||||
hasResult = badgeIsLegalCache.find(badgeId) != badgeIsLegalCache.end();
|
||||
result = hasResult ? badgeIsLegalCache[badgeId] : false;
|
||||
}
|
||||
if (hasResult)
|
||||
{
|
||||
// We've already awarded this badge during this game session (or the badge is no good), so you can't get it again.
|
||||
resumeFunction(result);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Make the request and send it out
|
||||
Http http(Aya::format(isBadgeLegalUrl.c_str(), badgeId, placeId));
|
||||
http.get(boost::bind(&BadgeService::isLegalResultHelper, weak_from(this), badgeId, _1, _2, resumeFunction, errorFunction));
|
||||
}
|
||||
|
||||
void BadgeService::awardBadgeResultHelper(weak_ptr<BadgeService> badgeService, int userId, int badgeId, std::string* response, std::exception* err,
|
||||
boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
if (shared_ptr<BadgeService> safeBadgeService = badgeService.lock())
|
||||
{
|
||||
safeBadgeService->awardBadgeResult(userId, badgeId, response, err, resumeFunction, errorFunction);
|
||||
}
|
||||
}
|
||||
|
||||
static void fireBadgeAwarded(shared_ptr<BadgeService> self, const std::string& response, int userId, int badgeId)
|
||||
{
|
||||
event_BadgeAwarded.fireAndReplicateEvent(self.get(), response, userId, badgeId);
|
||||
}
|
||||
|
||||
void BadgeService::awardBadgeResult(int userId, int badgeId, std::string* response, std::exception* err, boost::function<void(bool)> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
if (!response)
|
||||
{
|
||||
resumeFunction(false);
|
||||
return;
|
||||
}
|
||||
if (*response == "")
|
||||
{
|
||||
// We failed in some way to get data back, maybe we should try again? Or just give up?
|
||||
resumeFunction(false);
|
||||
}
|
||||
else if (*response == "0")
|
||||
{
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(badgeAwardSync);
|
||||
// It returned failure, they must already have the badge. Add them to the cache
|
||||
badgeAwardCache[userId].insert(badgeId);
|
||||
}
|
||||
resumeFunction(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(badgeAwardSync);
|
||||
// Cache that we gave them out this badge, so we won't try again
|
||||
badgeAwardCache[userId].insert(badgeId);
|
||||
}
|
||||
|
||||
Aya::DataModel* dataModel = (Aya::DataModel*)this->getParent();
|
||||
if (dataModel)
|
||||
{
|
||||
dataModel->submitTask(boost::bind(fireBadgeAwarded, shared_from(this), *response, userId, badgeId), DataModelJob::Write);
|
||||
}
|
||||
|
||||
resumeFunction(true);
|
||||
}
|
||||
}
|
||||
|
||||
void BadgeService::awardBadge(int userId, int badgeId, boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
if (!Workspace::serverIsPresent(this))
|
||||
{
|
||||
// Throw a friendly exception to the users that they can't get badges during user games
|
||||
StandardOut::singleton()->printf(MESSAGE_WARNING, "Sorry, badges can only be awarded by Kiseki game servers");
|
||||
resumeFunction(false);
|
||||
return;
|
||||
}
|
||||
|
||||
Network::Players* players = ServiceProvider::find<Network::Players>(this);
|
||||
AYAASSERT(players);
|
||||
|
||||
if (!players->getPlayerByID(userId))
|
||||
{
|
||||
StandardOut::singleton()->printf(MESSAGE_WARNING, "The player with userId=%d is not present at the moment", userId);
|
||||
resumeFunction(false);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
bool alreadyAwarded;
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock lock(badgeAwardSync);
|
||||
// Check if we have already awarded the badge to this user in this game session
|
||||
alreadyAwarded = (badgeAwardCache[userId].find(badgeId) != badgeAwardCache[userId].end());
|
||||
}
|
||||
if (alreadyAwarded)
|
||||
{
|
||||
StandardOut::singleton()->printf(MESSAGE_WARNING, "We already gave out badgeId=%d to userId=%d", badgeId, userId);
|
||||
// We've already awarded this badge during this game session (or the badge is no good), so you can't get it again.
|
||||
resumeFunction(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Make the request and send it out
|
||||
Http http(Aya::format(awardBadgeUrl.c_str(), userId, badgeId, placeId));
|
||||
|
||||
// switch get to post to fix exploit in badge service
|
||||
// http.get(boost::bind(&BadgeService::awardBadgeResultHelper, weak_from(this), userId, badgeId, _1, _2, resumeFunction, errorFunction));
|
||||
std::string in;
|
||||
http.post(in, Http::kContentTypeDefaultUnspecified, false,
|
||||
boost::bind(&BadgeService::awardBadgeResultHelper, weak_from(this), userId, badgeId, _1, _2, resumeFunction, errorFunction));
|
||||
}
|
||||
} // namespace Aya
|
||||
87
engine/app/src/DataModel/BadgeService.hpp
Normal file
87
engine/app/src/DataModel/BadgeService.hpp
Normal file
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#include "Tree/Service.hpp"
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
extern const char* const sBadgeService;
|
||||
|
||||
class BadgeService
|
||||
: public DescribedCreatable<BadgeService, Instance, sBadgeService, Reflection::ClassDescriptor::INTERNAL>
|
||||
, public Service
|
||||
{
|
||||
private:
|
||||
std::string awardBadgeUrl;
|
||||
std::string hasBadgeUrl;
|
||||
std::string isBadgeDisabledUrl;
|
||||
std::string isBadgeLegalUrl;
|
||||
|
||||
int placeId;
|
||||
int cooldownTime;
|
||||
|
||||
boost::recursive_mutex badgeAwardSync;
|
||||
std::map<int, std::set<int>> badgeAwardCache;
|
||||
|
||||
boost::recursive_mutex badgeQuerySync;
|
||||
std::map<int, std::set<int>> badgeQueryCache;
|
||||
|
||||
boost::recursive_mutex badgeIsDisabledSync;
|
||||
std::map<int, bool> badgeIsDisabledCache;
|
||||
|
||||
boost::recursive_mutex badgeIsLegalSync;
|
||||
std::map<int, bool> badgeIsLegalCache;
|
||||
|
||||
struct HotUserHasBadge
|
||||
{
|
||||
int userId;
|
||||
int badgeId;
|
||||
Aya::Time expiration;
|
||||
HotUserHasBadge(int userId, int badgeId, int cooldownTime);
|
||||
bool expired() const;
|
||||
};
|
||||
std::list<HotUserHasBadge> hotBadges;
|
||||
bool isHasBadgeHot(int userId, int badgeId);
|
||||
|
||||
public:
|
||||
BadgeService();
|
||||
~BadgeService() {}
|
||||
|
||||
Aya::remote_signal<void(std::string, int, int)> badgeAwardedSignal;
|
||||
|
||||
void userHasBadge(int userId, int badgeId, boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
void awardBadge(int userId, int badgeId, boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
void isDisabled(int badgeId, boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
void isLegal(int badgeId, boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
|
||||
void setPlaceId(int placeId);
|
||||
void setHasBadgeCooldown(int seconds);
|
||||
|
||||
void setAwardBadgeUrl(std::string);
|
||||
void setHasBadgeUrl(std::string);
|
||||
void setIsBadgeDisabledUrl(std::string);
|
||||
void setIsBadgeLegalUrl(std::string);
|
||||
|
||||
private:
|
||||
static void hasBadgeResultHelper(weak_ptr<BadgeService>, int userId, int badgeId, std::string* response, std::exception* err,
|
||||
boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
void hasBadgeResult(int userId, int badgeId, std::string* response, std::exception* err, boost::function<void(bool)> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction);
|
||||
|
||||
static void awardBadgeResultHelper(weak_ptr<BadgeService>, int userId, int badgeId, std::string* response, std::exception* err,
|
||||
boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
void awardBadgeResult(int userId, int badgeId, std::string* response, std::exception* err, boost::function<void(bool)> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction);
|
||||
|
||||
static void isDiabledResultHelper(weak_ptr<BadgeService>, int badgeId, std::string* response, std::exception* err,
|
||||
boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
void isDisabledResult(int badgeId, std::string* response, std::exception* err, boost::function<void(bool)> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction);
|
||||
|
||||
static void isLegalResultHelper(weak_ptr<BadgeService>, int badgeId, std::string* response, std::exception* err,
|
||||
boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
void isLegalResult(int badgeId, std::string* response, std::exception* err, boost::function<void(bool)> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction);
|
||||
};
|
||||
} // namespace Aya
|
||||
86
engine/app/src/DataModel/BaseRenderJob.cpp
Normal file
86
engine/app/src/DataModel/BaseRenderJob.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
|
||||
#include "DataModel/DataModel.hpp"
|
||||
#include "DataModel/BaseRenderJob.hpp"
|
||||
#include "DataModel/HackDefines.hpp"
|
||||
#include "DataModel/ModelInstance.hpp"
|
||||
#include "DataModel/Workspace.hpp"
|
||||
|
||||
#include "Profiler.hpp"
|
||||
#include "ImGui.hpp"
|
||||
|
||||
LOGGROUP(TaskSchedulerTiming)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
BaseRenderJob::BaseRenderJob(double minFps, double maxFps, boost::shared_ptr<DataModel> dataModel)
|
||||
: Aya::DataModelJob("Render", Aya::DataModelJob::Render, false, dataModel, Aya::Time::Interval(.02))
|
||||
, isAwake(true)
|
||||
, minFrameRate(minFps)
|
||||
, maxFrameRate(maxFps)
|
||||
{
|
||||
cyclicExecutive = true;
|
||||
// We originally intended RenderJob to run after Network, Physics, and so on
|
||||
// to reduce latency, however this introduced a weird variability into the dt between
|
||||
// each RenderJob::step. Since makes sure that the first job is always 16 - 17ms apart from
|
||||
// it's previous run, making RenderJob run first will grant more visual stability at the cost
|
||||
// of potentially some latency in input.
|
||||
cyclicPriority = CyclicExecutiveJobPriority_EarlyRendering;
|
||||
}
|
||||
|
||||
|
||||
void BaseRenderJob::wake()
|
||||
{
|
||||
isAwake = true;
|
||||
if (!isCyclicExecutiveJob())
|
||||
TaskScheduler::singleton().reschedule(shared_from_this());
|
||||
}
|
||||
|
||||
bool BaseRenderJob::tryJobAgain()
|
||||
{
|
||||
if (isCyclicExecutiveJob() && !isAwake)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BaseRenderJob::isCyclicExecutiveJob()
|
||||
{
|
||||
return Aya::TaskScheduler::singleton().isCyclicExecutive() && cyclicExecutive;
|
||||
}
|
||||
|
||||
TaskScheduler::Job::Error BaseRenderJob::error(const Stats& stats)
|
||||
{
|
||||
if (!isAwake && (!isCyclicExecutiveJob()))
|
||||
{
|
||||
return Job::Error();
|
||||
}
|
||||
|
||||
Job::Error result;
|
||||
if (isCyclicExecutiveJob())
|
||||
{
|
||||
result = computeStandardError(stats, maxFrameRate);
|
||||
FASTLOG1F(FLog::TaskSchedulerTiming, "Error recalculated, time since last call: %f", (float)stats.timespanSinceLastStep.seconds());
|
||||
}
|
||||
else
|
||||
{
|
||||
result = computeStandardError(stats, minFrameRate);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Time::Interval BaseRenderJob::timeSinceLastRender() const
|
||||
{
|
||||
return Time::now<Aya::Time::Fast>() - lastRenderTime;
|
||||
}
|
||||
|
||||
TaskScheduler::StepResult BaseRenderJob::step(const Stats& stats)
|
||||
{
|
||||
Profiler::onFrame();
|
||||
ImGui::onFrame();
|
||||
|
||||
return DataModelJob::step(stats);
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
41
engine/app/src/DataModel/BaseRenderJob.hpp
Normal file
41
engine/app/src/DataModel/BaseRenderJob.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/weak_ptr.hpp>
|
||||
|
||||
#include "DataModel/DataModel.hpp"
|
||||
#include "time.hpp"
|
||||
#include "TaskScheduler.hpp"
|
||||
#include "TaskScheduler.Job.hpp"
|
||||
#include "Utility/IMetric.hpp"
|
||||
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
class View;
|
||||
|
||||
// This is the base class for all Rendering Jobs. All rendering jobs will inherit from this.
|
||||
|
||||
class BaseRenderJob : public DataModelJob
|
||||
{
|
||||
protected:
|
||||
Time lastRenderTime;
|
||||
volatile bool isAwake;
|
||||
double minFrameRate;
|
||||
double maxFrameRate;
|
||||
|
||||
|
||||
public:
|
||||
BaseRenderJob(double minFrameRate, double maxFrameRate, boost::shared_ptr<DataModel> dataModel);
|
||||
|
||||
virtual void wake();
|
||||
virtual bool tryJobAgain();
|
||||
virtual bool isCyclicExecutiveJob();
|
||||
|
||||
virtual Time::Interval timeSinceLastRender() const;
|
||||
virtual Job::Error error(const Stats& stats);
|
||||
// virtual Aya::TaskScheduler::StepResult stepDataModelJob(const Stats&);
|
||||
|
||||
virtual TaskScheduler::StepResult step(const Stats& stats);
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
200
engine/app/src/DataModel/BasicPartInstance.cpp
Normal file
200
engine/app/src/DataModel/BasicPartInstance.cpp
Normal file
@@ -0,0 +1,200 @@
|
||||
|
||||
|
||||
|
||||
#include "DataModel/BasicPartInstance.hpp"
|
||||
#include "World/Primitive.hpp"
|
||||
|
||||
DYNAMIC_FASTFLAGVARIABLE(SpheresAllowedCustom, false)
|
||||
DYNAMIC_FASTFLAG(FormFactorDeprecated)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
const char* const sFormFactorPart = "FormFactorPart";
|
||||
const char* const sBasicPart = "Part";
|
||||
|
||||
using namespace Reflection;
|
||||
|
||||
const char* category_BasicPart = "Part ";
|
||||
|
||||
/*
|
||||
Unfortunately, we have 3 FormFactor properties. The reason for this
|
||||
is legacy content. For a while "FormFactor" was the STREAMING version
|
||||
and "formFactor" was the scriptable version. To support legacy files
|
||||
we override readProperty so that old files with FormFactor use the raw
|
||||
XML version of set.
|
||||
*/
|
||||
static const EnumPropDescriptor<FormFactorPart, FormFactorPart::FormFactor> prop_formFactorUi(
|
||||
"FormFactor", category_BasicPart, &FormFactorPart::getFormFactor, &FormFactorPart::setFormFactorUi, PropertyDescriptor::LEGACY_SCRIPTING);
|
||||
static const EnumPropDescriptor<FormFactorPart, FormFactorPart::FormFactor> prop_FormFactorLegacy(
|
||||
"formFactor", category_BasicPart, &FormFactorPart::getFormFactor, &FormFactorPart::setFormFactorUi, PropertyDescriptor::LEGACY_SCRIPTING);
|
||||
static const EnumPropDescriptor<FormFactorPart, FormFactorPart::FormFactor> prop_FormFactorData(
|
||||
"formFactorRaw", category_BasicPart, &FormFactorPart::getFormFactor, &FormFactorPart::setFormFactorXml, PropertyDescriptor::STREAMING);
|
||||
|
||||
FormFactorPart::FormFactorPart() {}
|
||||
|
||||
FormFactorPart::~FormFactorPart() {}
|
||||
|
||||
void FormFactorPart::readProperty(const XmlElement* propertyElement, IReferenceBinder& binder)
|
||||
{
|
||||
std::string name;
|
||||
if (propertyElement->findAttributeValue(name_name, name))
|
||||
{
|
||||
// Redirect legacy FormFactor to formFactorRaw
|
||||
if (name == "FormFactor")
|
||||
{
|
||||
prop_FormFactorData.readValue(this, propertyElement, binder);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Super::readProperty(propertyElement, binder);
|
||||
}
|
||||
|
||||
|
||||
void FormFactorPart::setFormFactorXml(FormFactor value)
|
||||
{
|
||||
if (value != formFactor)
|
||||
{
|
||||
formFactor = value;
|
||||
raisePropertyChanged(prop_FormFactorData);
|
||||
raisePropertyChanged(prop_formFactorUi);
|
||||
}
|
||||
}
|
||||
|
||||
void FormFactorPart::setFormFactorUi(FormFactor value)
|
||||
{
|
||||
validateFormFactor(value);
|
||||
|
||||
if (value != formFactor)
|
||||
{
|
||||
destroyJoints();
|
||||
setFormFactorXml(value);
|
||||
if (value != CUSTOM)
|
||||
{
|
||||
// Snap to a valid size
|
||||
Vector3 uiSize = getPartSizeUi();
|
||||
uiSize.y = std::max(1.0f, (float)Math::iRound(uiSize.y)); // trunc
|
||||
setPartSizeUnjoined(uiSize);
|
||||
}
|
||||
join();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
const EnumPropDescriptor<BasicPartInstance, BasicPartInstance::LegacyPartType> prop_shapXml(
|
||||
"shap", category_BasicPart, NULL, &BasicPartInstance::setLegacyPartTypeXml, PropertyDescriptor::LEGACY); // Used to prepare for TA14264
|
||||
const EnumPropDescriptor<BasicPartInstance, BasicPartInstance::LegacyPartType> BasicPartInstance::prop_shapeXml(
|
||||
"shape", category_BasicPart, &BasicPartInstance::getLegacyPartType, &BasicPartInstance::setLegacyPartTypeXml, PropertyDescriptor::STREAMING);
|
||||
static const EnumPropDescriptor<BasicPartInstance, BasicPartInstance::LegacyPartType> prop_shapeUi(
|
||||
"Shape", category_BasicPart, &BasicPartInstance::getLegacyPartType, &BasicPartInstance::setLegacyPartTypeUi, PropertyDescriptor::UI);
|
||||
|
||||
|
||||
BasicPartInstance::BasicPartInstance() {}
|
||||
|
||||
|
||||
|
||||
BasicPartInstance::~BasicPartInstance() {}
|
||||
|
||||
|
||||
void BasicPartInstance::validateFormFactor(FormFactor& formFactor)
|
||||
{
|
||||
if (DFFlag::SpheresAllowedCustom)
|
||||
{
|
||||
if (!hasThreeDimensionalSize() && formFactor != PartInstance::CUSTOM)
|
||||
{
|
||||
formFactor = PartInstance::SYMETRIC;
|
||||
}
|
||||
}
|
||||
else if (!hasThreeDimensionalSize())
|
||||
{
|
||||
formFactor = PartInstance::SYMETRIC;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PartType BasicPartInstance::getPartType() const
|
||||
{
|
||||
switch (legacyPartType)
|
||||
{
|
||||
case BLOCK_LEGACY_PART:
|
||||
return BLOCK_PART;
|
||||
case CYLINDER_LEGACY_PART:
|
||||
return CYLINDER_PART;
|
||||
case BALL_LEGACY_PART:
|
||||
return BALL_PART;
|
||||
default:
|
||||
AYAASSERT(0);
|
||||
return BLOCK_PART;
|
||||
}
|
||||
}
|
||||
|
||||
void BasicPartInstance::setLegacyPartTypeXml(LegacyPartType _type)
|
||||
{
|
||||
if (legacyPartType != _type)
|
||||
{
|
||||
legacyPartType = _type;
|
||||
|
||||
getPartPrimitive()->setGeometryType(legacyPartType == BLOCK_LEGACY_PART ? Geometry::GEOMETRY_BLOCK
|
||||
: legacyPartType == CYLINDER_LEGACY_PART ? Geometry::GEOMETRY_CYLINDER
|
||||
: Geometry::GEOMETRY_BALL);
|
||||
|
||||
if (!DFFlag::FormFactorDeprecated)
|
||||
{
|
||||
if (DFFlag::SpheresAllowedCustom)
|
||||
{
|
||||
if (!hasThreeDimensionalSize() && getFormFactor() != PartInstance::CUSTOM)
|
||||
{
|
||||
setFormFactorXml(PartInstance::SYMETRIC);
|
||||
}
|
||||
}
|
||||
else if (!hasThreeDimensionalSize())
|
||||
{
|
||||
setFormFactorXml(PartInstance::SYMETRIC);
|
||||
}
|
||||
}
|
||||
|
||||
raisePropertyChanged(prop_shapeXml);
|
||||
raisePropertyChanged(prop_shapeUi);
|
||||
|
||||
shouldRenderSetDirty();
|
||||
}
|
||||
}
|
||||
|
||||
void BasicPartInstance::setLegacyPartTypeUi(LegacyPartType _type)
|
||||
{
|
||||
if (!hasThreeDimensionalSize())
|
||||
{
|
||||
AYAASSERT(formFactor == PartInstance::SYMETRIC);
|
||||
}
|
||||
|
||||
if (legacyPartType != _type)
|
||||
{
|
||||
destroyJoints();
|
||||
|
||||
// 1. Set the new type / Make form factor symetric for balls, cylinders (form factor may change)
|
||||
setLegacyPartTypeXml(_type);
|
||||
|
||||
// 2. Make the dimensions uniform for balls, cyclinders (size may change)
|
||||
if (!hasThreeDimensionalSize())
|
||||
{
|
||||
setPartSizeUnjoined(getPartSizeUi());
|
||||
}
|
||||
safeMove();
|
||||
|
||||
join();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool BasicPartInstance::hasThreeDimensionalSize()
|
||||
{
|
||||
return legacyPartType != BALL_LEGACY_PART;
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
66
engine/app/src/DataModel/BasicPartInstance.hpp
Normal file
66
engine/app/src/DataModel/BasicPartInstance.hpp
Normal file
@@ -0,0 +1,66 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
extern const char* const sFormFactorPart;
|
||||
|
||||
|
||||
class FormFactorPart : public DescribedNonCreatable<FormFactorPart, PartInstance, sFormFactorPart>
|
||||
{
|
||||
typedef DescribedNonCreatable<FormFactorPart, PartInstance, sFormFactorPart> Super;
|
||||
|
||||
public:
|
||||
/*override*/ virtual FormFactor getFormFactor() const
|
||||
{
|
||||
return formFactor;
|
||||
}
|
||||
|
||||
void setFormFactorUi(FormFactor value);
|
||||
void setFormFactorXml(FormFactor value);
|
||||
|
||||
FormFactorPart();
|
||||
virtual ~FormFactorPart();
|
||||
|
||||
/* override */ void readProperty(const XmlElement* propertyElement, IReferenceBinder& binder);
|
||||
|
||||
protected:
|
||||
virtual void validateFormFactor(FormFactor& value) {}
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
extern const char* const sBasicPart;
|
||||
|
||||
class BasicPartInstance : public DescribedCreatable<BasicPartInstance, FormFactorPart, sBasicPart>
|
||||
{
|
||||
private:
|
||||
/*override*/ void validateFormFactor(FormFactor& value);
|
||||
|
||||
public:
|
||||
static const Reflection::EnumPropDescriptor<BasicPartInstance, LegacyPartType> prop_shapeXml;
|
||||
|
||||
BasicPartInstance();
|
||||
virtual ~BasicPartInstance();
|
||||
|
||||
/*override*/ virtual bool hasThreeDimensionalSize();
|
||||
|
||||
void setLegacyPartTypeUi(LegacyPartType _type);
|
||||
void setLegacyPartTypeXml(LegacyPartType _type);
|
||||
|
||||
/*override*/ virtual PartType getPartType() const;
|
||||
|
||||
LegacyPartType getLegacyPartType() const
|
||||
{
|
||||
return legacyPartType;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace Aya
|
||||
56
engine/app/src/DataModel/BevelMesh.cpp
Normal file
56
engine/app/src/DataModel/BevelMesh.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
|
||||
|
||||
|
||||
#include "DataModel/BevelMesh.hpp"
|
||||
|
||||
using namespace Aya;
|
||||
|
||||
const char* const Aya::sBevelMesh = "BevelMesh";
|
||||
|
||||
|
||||
static Reflection::PropDescriptor<BevelMesh, float> desc_bevel(
|
||||
"Bevel", category_Data, &BevelMesh::getBevel, &BevelMesh::setBevel, Aya::Reflection::PropertyDescriptor::STREAMING);
|
||||
static Reflection::PropDescriptor<BevelMesh, float> desc_roundness(
|
||||
"Bevel Roundness", category_Data, &BevelMesh::getRoundness, &BevelMesh::setRoundness, Aya::Reflection::PropertyDescriptor::STREAMING);
|
||||
static Reflection::PropDescriptor<BevelMesh, float> desc_bulge(
|
||||
"Bulge", category_Data, &BevelMesh::getBulge, &BevelMesh::setBulge, Aya::Reflection::PropertyDescriptor::STREAMING);
|
||||
REFLECTION_END();
|
||||
|
||||
BevelMesh::BevelMesh()
|
||||
: bevel(0.0)
|
||||
, roundness(0.0)
|
||||
, bulge(0.0)
|
||||
{
|
||||
}
|
||||
|
||||
const float BevelMesh::getBevel() const
|
||||
{
|
||||
return bevel;
|
||||
}
|
||||
|
||||
void BevelMesh::setBevel(const float bev)
|
||||
{
|
||||
bevel = bev;
|
||||
raisePropertyChanged(desc_bevel);
|
||||
}
|
||||
|
||||
const float BevelMesh::getRoundness() const
|
||||
{
|
||||
return roundness;
|
||||
}
|
||||
void BevelMesh::setRoundness(const float rnd)
|
||||
{
|
||||
roundness = rnd;
|
||||
raisePropertyChanged(desc_roundness);
|
||||
}
|
||||
|
||||
const float BevelMesh::getBulge() const
|
||||
{
|
||||
return bulge;
|
||||
}
|
||||
|
||||
void BevelMesh::setBulge(const float blg)
|
||||
{
|
||||
bulge = blg;
|
||||
raisePropertyChanged(desc_bulge);
|
||||
}
|
||||
28
engine/app/src/DataModel/BevelMesh.hpp
Normal file
28
engine/app/src/DataModel/BevelMesh.hpp
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DataModelMesh.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
extern const char* const sBevelMesh;
|
||||
class BevelMesh : public DescribedNonCreatable<BevelMesh, DataModelMesh, sBevelMesh>
|
||||
{
|
||||
protected:
|
||||
float bevel;
|
||||
float roundness;
|
||||
float bulge; // TODO : note, quick hack putting it in here.
|
||||
public:
|
||||
BevelMesh();
|
||||
|
||||
const float getRoundness() const;
|
||||
void setRoundness(const float roundness);
|
||||
|
||||
const float getBevel() const;
|
||||
void setBevel(const float bevel);
|
||||
|
||||
const float getBulge() const;
|
||||
void setBulge(const float bulge);
|
||||
};
|
||||
} // namespace Aya
|
||||
414
engine/app/src/DataModel/BillboardGui.cpp
Normal file
414
engine/app/src/DataModel/BillboardGui.cpp
Normal file
@@ -0,0 +1,414 @@
|
||||
|
||||
|
||||
#include "DataModel/BillboardGui.hpp"
|
||||
#include "DataModel/DataModel.hpp"
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
#include "DataModel/Filters.hpp"
|
||||
#include "World/ContactManager.hpp"
|
||||
#include "World/World.hpp"
|
||||
#include "Base/ViewportBillboarder.hpp"
|
||||
#include "Base/AdornBillboarder.hpp"
|
||||
#include "Base/AdornBillboarder2D.hpp"
|
||||
#include "Base/AdornSurface.hpp"
|
||||
#include "Player.hpp"
|
||||
#include "Players.hpp"
|
||||
|
||||
FASTFLAGVARIABLE(BillboardGuiVR, true)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
const char* const sAdornmentGui = "BillboardGui";
|
||||
const Reflection::RefPropDescriptor<BillboardGui, Instance> prop_adornee(
|
||||
"Adornee", category_Data, &BillboardGui::getAdorneeDangerous, &BillboardGui::setAdornee);
|
||||
Reflection::PropDescriptor<BillboardGui, Vector3> prop_studsOffset(
|
||||
"StudsOffset", category_Data, &BillboardGui::getStudsOffset, &BillboardGui::setStudsOffset);
|
||||
Reflection::PropDescriptor<BillboardGui, Vector3> prop_extentsOffset(
|
||||
"ExtentsOffset", category_Data, &BillboardGui::getExtentsOffset, &BillboardGui::setExtentsOffset);
|
||||
Reflection::PropDescriptor<BillboardGui, Vector2> prop_sizeOffset(
|
||||
"SizeOffset", category_Data, &BillboardGui::getSizeOffset, &BillboardGui::setSizeOffset);
|
||||
Reflection::PropDescriptor<BillboardGui, UDim2> prop_Size("Size", category_Data, &BillboardGui::getSize, &BillboardGui::setSize);
|
||||
Reflection::PropDescriptor<BillboardGui, bool> prop_Enabled("Enabled", category_Data, &BillboardGui::getEnabled, &BillboardGui::setEnabled);
|
||||
Reflection::PropDescriptor<BillboardGui, bool> prop_Active("Active", category_Data, &BillboardGui::getActive, &BillboardGui::setActive);
|
||||
Reflection::PropDescriptor<BillboardGui, bool> prop_AlwaysOnTop(
|
||||
"AlwaysOnTop", category_Data, &BillboardGui::getAlwaysOnTop, &BillboardGui::setAlwaysOnTop);
|
||||
const Reflection::RefPropDescriptor<BillboardGui, Instance> prop_LocalPlayerVisible(
|
||||
"PlayerToHideFrom", category_Data, &BillboardGui::getPlayerToHideFrom, &BillboardGui::setPlayerToHideFrom);
|
||||
|
||||
static bool getBillboardFrame(const Camera* camera, const CoordinateFrame& cameraCFrame, const Vector3& partSize, const CoordinateFrame& partCFrame,
|
||||
const Vector3& partExtentRelativeOffset, const Vector3& partStudsOffset, const Vector2& billboardSizeRelativeOffset, const UDim2& billboardSize,
|
||||
CoordinateFrame& result, Rect2D& viewport)
|
||||
{
|
||||
Extents localExtents = Extents::fromCenterCorner(Vector3(), partSize / 2);
|
||||
Extents cameraSpaceExtents = localExtents.toWorldSpace(cameraCFrame.inverse() * partCFrame);
|
||||
|
||||
Vector3 relativeOffsetInCameraSpace = (partExtentRelativeOffset + Vector3::one()) * 0.5f * cameraSpaceExtents.size() + cameraSpaceExtents.min();
|
||||
Vector3 billboardCenterInCameraSpace = relativeOffsetInCameraSpace + partStudsOffset;
|
||||
|
||||
Vector3 billboardCenterInWorldSpace = cameraCFrame.pointToWorldSpace(billboardCenterInCameraSpace);
|
||||
|
||||
float pixelsPerStud = 1;
|
||||
|
||||
{
|
||||
Vector3 billboardCenterInCameraSpace = camera->project(billboardCenterInWorldSpace);
|
||||
|
||||
if (billboardCenterInCameraSpace.z <= 0 || billboardCenterInCameraSpace.z > 1000)
|
||||
return false;
|
||||
|
||||
pixelsPerStud = billboardCenterInCameraSpace.z;
|
||||
}
|
||||
|
||||
float studsPerPixel = 1.f / pixelsPerStud; // undo the perspective effect of finding extents in screenspace.
|
||||
|
||||
// -1 : to UI coordinates (0,0 upper left, )
|
||||
Vector2 billboardSizeInStuds = (billboardSize * Vector2(pixelsPerStud, pixelsPerStud)) * studsPerPixel;
|
||||
viewport = Rect2D(billboardSizeInStuds * pixelsPerStud);
|
||||
|
||||
Vector2 UIToCameraSpaceScaler(billboardSizeInStuds / viewport.wh());
|
||||
|
||||
billboardCenterInCameraSpace += Vector3(billboardSizeRelativeOffset * billboardSizeInStuds, 0);
|
||||
|
||||
CoordinateFrame desiredModelView;
|
||||
desiredModelView.translation = billboardCenterInCameraSpace - Vector3(billboardSizeInStuds.x * 0.5f, billboardSizeInStuds.y * -0.5f, 0);
|
||||
desiredModelView.rotation.set(UIToCameraSpaceScaler.x, 0, 0, 0, UIToCameraSpaceScaler.y, 0, 0, 0, 1);
|
||||
|
||||
result = cameraCFrame * desiredModelView;
|
||||
return true;
|
||||
}
|
||||
|
||||
// This is Moeller<65>Trumbore ray-triangle intersection algorithm, adapted for quads
|
||||
// The only difference with quads is the barycentric coordinate boundary check.
|
||||
static bool rayQuad(
|
||||
const Vector3& raySource, const Vector3& rayDir, const Vector3& v0, const Vector3& v1, const Vector3& v2, float& t, float& u, float& v)
|
||||
{
|
||||
Vector3 edge1 = v1 - v0;
|
||||
Vector3 edge2 = v2 - v0;
|
||||
Vector3 P = rayDir.cross(edge2);
|
||||
|
||||
float det = edge1.dot(P);
|
||||
|
||||
if (det <= 0)
|
||||
return false;
|
||||
|
||||
Vector3 T = raySource - v0;
|
||||
Vector3 Q = T.cross(edge1);
|
||||
|
||||
t = edge2.dot(Q) / det;
|
||||
u = T.dot(P) / det;
|
||||
v = rayDir.dot(Q) / det;
|
||||
|
||||
return t >= 0 && u >= 0 && u <= 1 && v >= 0 && v <= 1;
|
||||
}
|
||||
|
||||
static bool getBillboardHit(Workspace* workspace, const Vector2& pos, const RbxRay& ray, const Vector2& viewport,
|
||||
const CoordinateFrame& projectionFrame, bool alwaysOnTop, Vector2& result)
|
||||
{
|
||||
Vector3 v0 = projectionFrame.pointToWorldSpace(Vector3(0, 0, 0));
|
||||
Vector3 v1 = projectionFrame.pointToWorldSpace(Vector3(0, -viewport.y, 0));
|
||||
Vector3 v2 = projectionFrame.pointToWorldSpace(Vector3(viewport.x, 0, 0));
|
||||
|
||||
float t, u, v;
|
||||
if (!rayQuad(ray.origin(), ray.direction(), v0, v1, v2, t, u, v))
|
||||
return false;
|
||||
|
||||
result.x = v * viewport.x;
|
||||
result.y = u * viewport.y;
|
||||
|
||||
if (alwaysOnTop)
|
||||
return true;
|
||||
|
||||
RbxRay searchRay = RbxRay::fromOriginAndDirection(ray.origin(), ray.direction() * 2048);
|
||||
|
||||
FilterInvisibleNonColliding filter; // invisible & non-colliding parts don't block mouse-clicks
|
||||
|
||||
Vector3 hitPoint;
|
||||
if (workspace->getWorld()->getContactManager()->getHit(searchRay, NULL, &filter, hitPoint) == NULL)
|
||||
return true;
|
||||
|
||||
float wt = ray.direction().dot(hitPoint - ray.origin());
|
||||
|
||||
return (t < wt);
|
||||
}
|
||||
|
||||
BillboardGui::BillboardGui()
|
||||
: DescribedCreatable<BillboardGui, GuiLayerCollector, sAdornmentGui>("BillboardGui")
|
||||
, IStepped(StepType_Render)
|
||||
, alwaysOnTop(false)
|
||||
, enabled(true)
|
||||
, active(false)
|
||||
, visibleAndValid(false)
|
||||
{
|
||||
if (!FFlag::BillboardGuiVR)
|
||||
viewportBillboarder.reset(new ViewportBillboarder());
|
||||
}
|
||||
|
||||
void BillboardGui::setRenderFunction(boost::function<void(BillboardGui*, Adorn*)> func)
|
||||
{
|
||||
adornFunc = func;
|
||||
}
|
||||
|
||||
bool BillboardGui::askSetParent(const Instance* instance) const
|
||||
{
|
||||
return (Instance::fastDynamicCast<GuiBase2d>(instance) == NULL);
|
||||
}
|
||||
|
||||
void BillboardGui::onStepped(const Stepped& event)
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
shared_ptr<Instance> part = getPart();
|
||||
Workspace* workspace = ServiceProvider::find<Workspace>(part.get());
|
||||
|
||||
if (part && workspace)
|
||||
{
|
||||
CoordinateFrame adornCFrame;
|
||||
Vector3 adornSize;
|
||||
|
||||
calcAdornPlacement(part.get(), adornCFrame, adornSize);
|
||||
|
||||
{
|
||||
const Camera* camera = workspace->getConstCamera();
|
||||
CoordinateFrame cameraCFrame = camera->getRenderingCoordinateFrame();
|
||||
|
||||
UserInputService* uis = ServiceProvider::find<UserInputService>(workspace);
|
||||
|
||||
{
|
||||
visibleAndValid = getBillboardFrame(camera, cameraCFrame, adornSize, adornCFrame, partExtentRelativeOffset, partStudsOffset,
|
||||
billboardSizeRelativeOffset, billboardSize, projectionFrame, viewport);
|
||||
}
|
||||
|
||||
if (visibleAndValid)
|
||||
handleResize(viewport, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool BillboardGui::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> BillboardGui::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 BillboardGui::render3dSortedPosition() const
|
||||
{
|
||||
boost::shared_ptr<Instance> part = getPart();
|
||||
|
||||
CoordinateFrame cframe;
|
||||
Vector3 size;
|
||||
calcAdornPlacement(part.get(), cframe, size);
|
||||
|
||||
return cframe.translation;
|
||||
}
|
||||
|
||||
void BillboardGui::render3dSortedAdorn(Adorn* adorn)
|
||||
{
|
||||
{
|
||||
if (!enabled || !visibleAndValid)
|
||||
return;
|
||||
|
||||
boost::shared_ptr<Instance> part = getPart();
|
||||
Workspace* workspace = ServiceProvider::find<Workspace>(part.get());
|
||||
|
||||
if (!part || !workspace)
|
||||
return;
|
||||
|
||||
if (Aya::Network::Player* player = Network::Players::findLocalPlayer(DataModel::get(part.get())))
|
||||
if (shared_ptr<Instance> noPlayerRender = playerToHideFrom.lock())
|
||||
if (player == fastDynamicCast<Aya::Network::Player>(noPlayerRender.get()))
|
||||
return;
|
||||
|
||||
{
|
||||
Vector3 screenOffset = workspace->getConstCamera()->project(projectionFrame.translation);
|
||||
|
||||
AdornBillboarder2D adornView(adorn, viewport, Math::roundVector2(screenOffset.xy()));
|
||||
Super::render2d(&adornView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GuiResponse BillboardGui::process(const shared_ptr<InputObject>& event)
|
||||
{
|
||||
{
|
||||
if (!enabled || !active || !visibleAndValid)
|
||||
return GuiResponse::notSunk();
|
||||
|
||||
if (event->isMouseEvent())
|
||||
{
|
||||
if (Workspace* workspace = ServiceProvider::find<Workspace>(this))
|
||||
{
|
||||
Vector2 pos = event->get2DPosition();
|
||||
RbxRay ray = workspace->getCamera()->worldRay(pos.x, pos.y);
|
||||
|
||||
Vector2 hit;
|
||||
if (getBillboardHit(workspace, pos, ray, viewport.wh(), projectionFrame, alwaysOnTop, hit))
|
||||
{
|
||||
shared_ptr<InputObject> transformedEvent = Aya::Creatable<Instance>::create<InputObject>(*event);
|
||||
transformedEvent->setPosition(Vector3(Math::roundVector2(hit), 0));
|
||||
return Super::process(transformedEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GuiResponse::notSunk();
|
||||
}
|
||||
}
|
||||
|
||||
bool BillboardGui::canProcessMeAndDescendants() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void BillboardGui::setPlayerToHideFrom(Instance* value)
|
||||
{
|
||||
if (value && !fastDynamicCast<Aya::Network::Player>(value))
|
||||
throw std::runtime_error("HideFromPlayer can only be of type Player");
|
||||
|
||||
if (playerToHideFrom.lock().get() != value)
|
||||
{
|
||||
playerToHideFrom = shared_from(value);
|
||||
raisePropertyChanged(prop_LocalPlayerVisible);
|
||||
shouldRenderSetDirty();
|
||||
}
|
||||
}
|
||||
|
||||
void BillboardGui::setEnabled(bool value)
|
||||
{
|
||||
if (enabled != value)
|
||||
{
|
||||
enabled = value;
|
||||
raisePropertyChanged(prop_Enabled);
|
||||
shouldRenderSetDirty();
|
||||
}
|
||||
}
|
||||
|
||||
void BillboardGui::setActive(bool value)
|
||||
{
|
||||
if (active != value)
|
||||
{
|
||||
active = value;
|
||||
raisePropertyChanged(prop_Active);
|
||||
}
|
||||
}
|
||||
|
||||
void BillboardGui::setAdornee(Instance* value)
|
||||
{
|
||||
if (adornee.lock().get() != value)
|
||||
{
|
||||
adornee = shared_from(value);
|
||||
raisePropertyChanged(prop_adornee);
|
||||
shouldRenderSetDirty();
|
||||
}
|
||||
}
|
||||
|
||||
void BillboardGui::onAncestorChanged(const AncestorChanged& event)
|
||||
{
|
||||
Super::onAncestorChanged(event);
|
||||
shouldRenderSetDirty();
|
||||
}
|
||||
|
||||
const Vector3& BillboardGui::getStudsOffset() const
|
||||
{
|
||||
return partStudsOffset;
|
||||
}
|
||||
|
||||
void BillboardGui::setStudsOffset(const Vector3& value)
|
||||
{
|
||||
if (partStudsOffset != value)
|
||||
{
|
||||
partStudsOffset = value;
|
||||
raisePropertyChanged(prop_studsOffset);
|
||||
}
|
||||
}
|
||||
|
||||
const Vector3& BillboardGui::getExtentsOffset() const
|
||||
{
|
||||
return partExtentRelativeOffset;
|
||||
}
|
||||
|
||||
void BillboardGui::setExtentsOffset(const Vector3& value)
|
||||
{
|
||||
if (partExtentRelativeOffset != value)
|
||||
{
|
||||
partExtentRelativeOffset = value;
|
||||
raisePropertyChanged(prop_extentsOffset);
|
||||
}
|
||||
}
|
||||
|
||||
const Vector2& BillboardGui::getSizeOffset() const
|
||||
{
|
||||
return billboardSizeRelativeOffset;
|
||||
}
|
||||
|
||||
void BillboardGui::setSizeOffset(const Vector2& value)
|
||||
{
|
||||
if (billboardSizeRelativeOffset != value)
|
||||
{
|
||||
billboardSizeRelativeOffset = value;
|
||||
raisePropertyChanged(prop_sizeOffset);
|
||||
}
|
||||
}
|
||||
|
||||
UDim2 BillboardGui::getSize() const
|
||||
{
|
||||
return billboardSize;
|
||||
}
|
||||
|
||||
void BillboardGui::setSize(UDim2 value)
|
||||
{
|
||||
if (billboardSize != value)
|
||||
{
|
||||
billboardSize = value;
|
||||
raisePropertyChanged(prop_Size);
|
||||
}
|
||||
}
|
||||
|
||||
bool BillboardGui::getAlwaysOnTop() const
|
||||
{
|
||||
return alwaysOnTop;
|
||||
}
|
||||
|
||||
void BillboardGui::setAlwaysOnTop(bool value)
|
||||
{
|
||||
if (alwaysOnTop != value)
|
||||
{
|
||||
alwaysOnTop = value;
|
||||
raisePropertyChanged(prop_AlwaysOnTop);
|
||||
}
|
||||
}
|
||||
|
||||
void BillboardGui::calcAdornPlacement(Instance* part, CoordinateFrame& cframe, Vector3& size) const
|
||||
{
|
||||
if (PartInstance* thePart = fastDynamicCast<PartInstance>(part))
|
||||
{
|
||||
cframe = thePart->calcRenderingCoordinateFrame();
|
||||
size = thePart->getPartSizeXml();
|
||||
}
|
||||
else if (ModelInstance* theModel = fastDynamicCast<ModelInstance>(part))
|
||||
{
|
||||
cframe = theModel->calculateModelCFrame();
|
||||
size = theModel->calculateModelSize();
|
||||
}
|
||||
else
|
||||
{
|
||||
cframe = CoordinateFrame();
|
||||
size = Vector3(0, 0, 0);
|
||||
}
|
||||
}
|
||||
} // namespace Aya
|
||||
145
engine/app/src/DataModel/BillboardGui.hpp
Normal file
145
engine/app/src/DataModel/BillboardGui.hpp
Normal file
@@ -0,0 +1,145 @@
|
||||
#pragma once
|
||||
|
||||
#include "DataModel/GuiBase2d.hpp"
|
||||
#include "Utility/UDim.hpp"
|
||||
#include "Utility/SteppedInstance.hpp"
|
||||
#include "DataModel/GuiLayerCollector.hpp"
|
||||
#include "DataModel/ModelInstance.hpp"
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
class PartInstance;
|
||||
class ViewportBillboarder;
|
||||
|
||||
extern const char* const sAdornmentGui;
|
||||
|
||||
// A core window on which other windows are created
|
||||
// Ugh: this component is also a "PartAdornment", but we can't easily derive from both Adornment and GuiBase2d, resolve later.
|
||||
class BillboardGui
|
||||
: public DescribedCreatable<BillboardGui, GuiLayerCollector, sAdornmentGui>
|
||||
, public IStepped
|
||||
{
|
||||
private:
|
||||
typedef DescribedCreatable<BillboardGui, GuiLayerCollector, sAdornmentGui> Super;
|
||||
std::auto_ptr<ViewportBillboarder> viewportBillboarder;
|
||||
|
||||
public:
|
||||
BillboardGui();
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
// Instance
|
||||
//
|
||||
/*override*/ bool askSetParent(const Instance* instance) const;
|
||||
|
||||
/*override*/ bool processMeAndDescendants() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const Instance* getAdornee() const
|
||||
{
|
||||
return adornee.lock().get();
|
||||
}
|
||||
Instance* getAdornee()
|
||||
{
|
||||
return adornee.lock().get();
|
||||
}
|
||||
void setAdornee(Instance* value);
|
||||
Instance* getAdorneeDangerous() const
|
||||
{
|
||||
return adornee.lock().get();
|
||||
}
|
||||
|
||||
const Vector3& getStudsOffset() const;
|
||||
void setStudsOffset(const Vector3& value);
|
||||
|
||||
const Vector3& getExtentsOffset() const;
|
||||
void setExtentsOffset(const Vector3& value);
|
||||
|
||||
const Vector2& getSizeOffset() const;
|
||||
void setSizeOffset(const Vector2& value);
|
||||
|
||||
UDim2 getSize() const;
|
||||
void setSize(UDim2 value);
|
||||
|
||||
bool getAlwaysOnTop() const;
|
||||
void setAlwaysOnTop(bool value);
|
||||
|
||||
bool getActive() const
|
||||
{
|
||||
return active;
|
||||
}
|
||||
void setActive(bool value);
|
||||
|
||||
bool getEnabled() const
|
||||
{
|
||||
return enabled;
|
||||
}
|
||||
void setEnabled(bool value);
|
||||
|
||||
void setRenderFunction(boost::function<void(BillboardGui*, Adorn*)> func);
|
||||
|
||||
Instance* getPlayerToHideFrom() const
|
||||
{
|
||||
return playerToHideFrom.lock().get();
|
||||
}
|
||||
void setPlayerToHideFrom(Instance* value);
|
||||
|
||||
private:
|
||||
boost::shared_ptr<Instance> getPart() const;
|
||||
|
||||
boost::function<void(BillboardGui*, Adorn*)> adornFunc;
|
||||
|
||||
// reflected state
|
||||
Vector3 partExtentRelativeOffset;
|
||||
Vector3 partStudsOffset;
|
||||
Vector2 billboardSizeRelativeOffset;
|
||||
UDim2 billboardSize;
|
||||
|
||||
bool alwaysOnTop;
|
||||
bool enabled;
|
||||
bool active;
|
||||
|
||||
// dynamic state
|
||||
bool visibleAndValid;
|
||||
CoordinateFrame projectionFrame;
|
||||
Rect2D viewport;
|
||||
|
||||
// reflected state
|
||||
boost::weak_ptr<Instance> playerToHideFrom;
|
||||
boost::weak_ptr<Instance> adornee;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Instance
|
||||
/*override*/ void onAncestorChanged(const AncestorChanged& event);
|
||||
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
|
||||
{
|
||||
Super::onServiceProvider(oldProvider, newProvider);
|
||||
onServiceProviderIStepped(oldProvider, newProvider);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IAdornable
|
||||
/*override*/ bool shouldRender3dSortedAdorn() const;
|
||||
/*override*/ void render3dSortedAdorn(Adorn* adorn);
|
||||
/*override*/ Vector3 render3dSortedPosition() const;
|
||||
/*override*/ bool isVisible(const Rect2D& rect) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// GuiTarget
|
||||
/*override*/ bool canProcessMeAndDescendants() const;
|
||||
/*override*/ GuiResponse process(const shared_ptr<InputObject>& event);
|
||||
|
||||
// IStepped
|
||||
virtual void onStepped(const Stepped& event);
|
||||
|
||||
void calcAdornPlacement(Instance* part, CoordinateFrame& cframe, Vector3& size) const;
|
||||
};
|
||||
} // namespace Aya
|
||||
90
engine/app/src/DataModel/Bindable.cpp
Normal file
90
engine/app/src/DataModel/Bindable.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
|
||||
|
||||
#include "DataModel/Bindable.hpp"
|
||||
#include "FastLog.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
|
||||
const char* const sBindableFunction = "BindableFunction";
|
||||
static Reflection::BoundYieldFuncDesc<BindableFunction, shared_ptr<const Reflection::Tuple>(shared_ptr<const Reflection::Tuple>)> func_Invoke(
|
||||
&BindableFunction::invoke, "Invoke", "arguments", Security::None);
|
||||
static Reflection::BoundAsyncCallbackDesc<BindableFunction, shared_ptr<const Reflection::Tuple>(shared_ptr<const Reflection::Tuple>)>
|
||||
callback_OnInvoke("OnInvoke", &BindableFunction::onInvoke, "arguments", &BindableFunction::processQueue);
|
||||
|
||||
#if 0
|
||||
const char* const sPropertyInstance = "Property";
|
||||
static Reflection::PropDescriptor<PropertyInstance, Reflection::Variant> prop_Value("Value", category_Data, &PropertyInstance::getValue, &PropertyInstance::setValue, Reflection::PropertyDescriptor::SCRIPTING);
|
||||
static Reflection::BoundCallbackDesc<Reflection::Variant()> callback_GetValue("OnGetValue", &PropertyInstance::getCallback);
|
||||
static Reflection::BoundCallbackDesc<void(Reflection::Variant)> callback_SetValue("OnSetValue", &PropertyInstance::setCallback, "value");
|
||||
static Reflection::EventDesc<PropertyInstance, void(Reflection::Variant)> event_ValueChanged(&PropertyInstance::valueChanged, "ValueChanged", "value");
|
||||
static Reflection::BoundFuncDesc<PropertyInstance, void()> func_FireValueChanged(&PropertyInstance::fireValueChanged, "FireValueChanged", Security::None);
|
||||
#endif
|
||||
|
||||
const char* const sBindableEvent = "BindableEvent";
|
||||
static Reflection::BoundFuncDesc<BindableEvent, void(shared_ptr<const Reflection::Tuple>)> func_Fire(
|
||||
&BindableEvent::fire, "Fire", "arguments", Security::None);
|
||||
static Reflection::EventDesc<BindableEvent, void(shared_ptr<const Reflection::Tuple>)> event_Event(&BindableEvent::event, "Event", "arguments");
|
||||
REFLECTION_END();
|
||||
|
||||
void BindableFunction::processQueue(const OnInvokeCallback& oldValue)
|
||||
{
|
||||
if (onInvoke.empty())
|
||||
return;
|
||||
|
||||
while (!queue.empty())
|
||||
{
|
||||
// Copy it to the stack to avoid re-entrancy bugs
|
||||
const Invocation invocation = queue.front();
|
||||
queue.pop();
|
||||
|
||||
onInvoke(invocation.arguments, invocation.resumeFunction, invocation.errorFunction);
|
||||
}
|
||||
}
|
||||
|
||||
void BindableFunction::invoke(shared_ptr<const Reflection::Tuple> arguments,
|
||||
boost::function<void(shared_ptr<const Reflection::Tuple>)> resumeFunction, boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
if (onInvoke.empty())
|
||||
{
|
||||
Invocation invocation = {arguments, resumeFunction, errorFunction};
|
||||
queue.push(invocation);
|
||||
return;
|
||||
}
|
||||
|
||||
onInvoke(arguments, resumeFunction, errorFunction);
|
||||
}
|
||||
|
||||
bool BindableFunction::askSetParent(const Instance* instance) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void BindableEvent::fire(shared_ptr<const Reflection::Tuple> arguments)
|
||||
{
|
||||
event(arguments);
|
||||
}
|
||||
|
||||
bool BindableEvent::askSetParent(const Instance* instance) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
void PropertyInstance::fireValueChanged()
|
||||
{
|
||||
valueChanged(getCallback());
|
||||
raisePropertyChanged(prop_Value);
|
||||
}
|
||||
Reflection::Variant PropertyInstance::getValue()
|
||||
{
|
||||
return getCallback();
|
||||
}
|
||||
void PropertyInstance::setValue(const Reflection::Variant& value)
|
||||
{
|
||||
setCallback(value);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Aya
|
||||
76
engine/app/src/DataModel/Bindable.hpp
Normal file
76
engine/app/src/DataModel/Bindable.hpp
Normal file
@@ -0,0 +1,76 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Tree/Instance.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
// Summary: A simple class that exposes a configurable function.
|
||||
// Usage: It allows scripts to expose their functions
|
||||
extern const char* const sBindableFunction;
|
||||
class BindableFunction : public DescribedCreatable<BindableFunction, Instance, sBindableFunction>
|
||||
{
|
||||
struct Invocation
|
||||
{
|
||||
shared_ptr<const Reflection::Tuple> arguments;
|
||||
boost::function<void(shared_ptr<const Reflection::Tuple>)> resumeFunction;
|
||||
boost::function<void(std::string)> errorFunction;
|
||||
};
|
||||
typedef std::queue<Invocation> Queue;
|
||||
Queue queue;
|
||||
|
||||
public:
|
||||
BindableFunction()
|
||||
: DescribedCreatable<BindableFunction, Instance, sBindableFunction>("Function")
|
||||
{
|
||||
}
|
||||
|
||||
void invoke(shared_ptr<const Reflection::Tuple> arguments, boost::function<void(shared_ptr<const Reflection::Tuple>)> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction);
|
||||
typedef boost::function<void(
|
||||
shared_ptr<const Reflection::Tuple>, boost::function<void(shared_ptr<const Reflection::Tuple>)>, boost::function<void(std::string)>)>
|
||||
OnInvokeCallback;
|
||||
OnInvokeCallback onInvoke;
|
||||
void processQueue(const OnInvokeCallback& oldValue);
|
||||
/*override*/ bool askSetParent(const Instance* instance) const;
|
||||
};
|
||||
|
||||
#if 0
|
||||
// Summary: A simple class that exposes a property
|
||||
// Usage: It allows scripts to expose their properties
|
||||
extern const char* const sPropertyInstance;
|
||||
class PropertyInstance : public DescribedCreatable<PropertyInstance, Instance, sPropertyInstance>
|
||||
{
|
||||
public:
|
||||
PropertyInstance():DescribedCreatable<PropertyInstance, Instance, sPropertyInstance>("Property") {}
|
||||
|
||||
Aya::signal<void(Reflection::Variant)> valueChanged;
|
||||
boost::function<Reflection::Variant()> getCallback;
|
||||
boost::function<void(Reflection::Variant)> setCallback;
|
||||
|
||||
Reflection::Variant getValue();
|
||||
void setValue(const Reflection::Variant& value);
|
||||
|
||||
void fireValueChanged();
|
||||
};
|
||||
#endif
|
||||
|
||||
// Summary: A simple class that exposes a fire-able event.
|
||||
// Usage: It allows scripts to expose their events
|
||||
extern const char* const sBindableEvent;
|
||||
class BindableEvent : public DescribedCreatable<BindableEvent, Instance, sBindableEvent>
|
||||
{
|
||||
public:
|
||||
BindableEvent()
|
||||
: DescribedCreatable<BindableEvent, Instance, sBindableEvent>("Event")
|
||||
{
|
||||
}
|
||||
Aya::signal<void(shared_ptr<const Reflection::Tuple>)> event;
|
||||
void fire(shared_ptr<const Reflection::Tuple> arguments);
|
||||
/*override*/ bool askSetParent(const Instance* instance) const;
|
||||
};
|
||||
|
||||
|
||||
} // namespace Aya
|
||||
8
engine/app/src/DataModel/BlockMesh.cpp
Normal file
8
engine/app/src/DataModel/BlockMesh.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
|
||||
|
||||
#include "DataModel/BlockMesh.hpp"
|
||||
|
||||
using namespace Aya;
|
||||
|
||||
const char* const Aya::sBlockMesh = "BlockMesh";
|
||||
15
engine/app/src/DataModel/BlockMesh.hpp
Normal file
15
engine/app/src/DataModel/BlockMesh.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BevelMesh.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
extern const char* const sBlockMesh;
|
||||
class BlockMesh : public DescribedCreatable<BlockMesh, BevelMesh, sBlockMesh>
|
||||
{
|
||||
public:
|
||||
BlockMesh() {}
|
||||
};
|
||||
} // namespace Aya
|
||||
53
engine/app/src/DataModel/BloomEffect.cpp
Normal file
53
engine/app/src/DataModel/BloomEffect.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "BloomEffect.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
const char* const sBloomEffect = "BloomEffect";
|
||||
|
||||
static Reflection::PropDescriptor<BloomEffect, float> prop_brightness("Intensity", "State", &BloomEffect::getIntensity, &BloomEffect::setIntensity, Reflection::PropertyDescriptor::STANDARD);
|
||||
static Reflection::PropDescriptor<BloomEffect, float> prop_contrast("Size", "State", &BloomEffect::getSize, &BloomEffect::setSize, Reflection::PropertyDescriptor::STANDARD);
|
||||
static Reflection::PropDescriptor<BloomEffect, float> prop_saturation("Threshold", "State", &BloomEffect::getThreshold, &BloomEffect::setThreshold, Reflection::PropertyDescriptor::STANDARD);
|
||||
|
||||
|
||||
BloomEffect::BloomEffect()
|
||||
: m_intensity(1)
|
||||
, m_size(24)
|
||||
, m_threshold(2)
|
||||
{
|
||||
setName(sBloomEffect);
|
||||
}
|
||||
|
||||
BloomEffect::~BloomEffect()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
void BloomEffect::setIntensity(float intensity)
|
||||
{
|
||||
if (m_intensity == intensity)
|
||||
return;
|
||||
|
||||
m_intensity = intensity;
|
||||
raisePropertyChanged(prop_brightness);
|
||||
}
|
||||
|
||||
void BloomEffect::setSize(float size)
|
||||
{
|
||||
if (m_size == size)
|
||||
return;
|
||||
|
||||
m_size = size;
|
||||
raisePropertyChanged(prop_contrast);
|
||||
}
|
||||
|
||||
void BloomEffect::setThreshold(float threshold)
|
||||
{
|
||||
if (m_threshold == threshold)
|
||||
return;
|
||||
|
||||
m_threshold = threshold;
|
||||
raisePropertyChanged(prop_saturation);
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
29
engine/app/src/DataModel/BloomEffect.hpp
Normal file
29
engine/app/src/DataModel/BloomEffect.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "PostEffect.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
extern const char* const sBloomEffect;
|
||||
|
||||
class BloomEffect : public DescribedCreatable<BloomEffect, PostEffect, sBloomEffect>
|
||||
{
|
||||
public:
|
||||
BloomEffect();
|
||||
~BloomEffect();
|
||||
|
||||
void setIntensity(float intensity);
|
||||
void setSize(float size);
|
||||
void setThreshold(float threshold);
|
||||
|
||||
float getIntensity() const { return m_intensity; }
|
||||
float getSize() const { return m_size; }
|
||||
float getThreshold() const { return m_threshold; }
|
||||
private:
|
||||
float m_intensity;
|
||||
float m_size;
|
||||
float m_threshold;
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
30
engine/app/src/DataModel/BlurEffect.cpp
Normal file
30
engine/app/src/DataModel/BlurEffect.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#include "BlurEffect.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
const char* const sBlurEffect = "BlurEffect";
|
||||
|
||||
static Reflection::PropDescriptor<BlurEffect, float> prop_size("Size", "State", &BlurEffect::getSize, &BlurEffect::setSize, Reflection::PropertyDescriptor::STANDARD);
|
||||
|
||||
BlurEffect::BlurEffect()
|
||||
: m_size(4.0f)
|
||||
{
|
||||
setName(sBlurEffect);
|
||||
}
|
||||
|
||||
BlurEffect::~BlurEffect()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
void BlurEffect::setSize(float size)
|
||||
{
|
||||
if (m_size == size)
|
||||
return;
|
||||
|
||||
m_size = size;
|
||||
raisePropertyChanged(prop_size);
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
22
engine/app/src/DataModel/BlurEffect.hpp
Normal file
22
engine/app/src/DataModel/BlurEffect.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "PostEffect.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
extern const char* const sBlurEffect;
|
||||
|
||||
class BlurEffect : public DescribedCreatable<BlurEffect, PostEffect, sBlurEffect>
|
||||
{
|
||||
public:
|
||||
BlurEffect();
|
||||
~BlurEffect();
|
||||
|
||||
void setSize(float size);
|
||||
float getSize() const { return m_size; }
|
||||
private:
|
||||
float m_size = 4.0f;
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
242
engine/app/src/DataModel/CSGDictionaryService.cpp
Normal file
242
engine/app/src/DataModel/CSGDictionaryService.cpp
Normal file
@@ -0,0 +1,242 @@
|
||||
|
||||
|
||||
|
||||
#include "DataModel/CSGDictionaryService.hpp"
|
||||
#include "DataModel/PartOperation.hpp"
|
||||
#include "DataModel/Value.hpp"
|
||||
#include "DataModel/DataModel.hpp"
|
||||
#include "DataModel/NonReplicatedCSGDictionaryService.hpp"
|
||||
|
||||
FASTFLAG(StudioCSGAssets)
|
||||
FASTFLAG(CSGLoadFromCDN)
|
||||
FASTFLAG(IgnoreBlankDataOnStore)
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
const std::string localKeyTag("CSGK");
|
||||
const size_t minKeySize = 4;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
const char* const sCSGDictionaryService = "CSGDictionaryService";
|
||||
|
||||
CSGDictionaryService::CSGDictionaryService()
|
||||
{
|
||||
setName("CSGDictionaryService");
|
||||
}
|
||||
|
||||
void CSGDictionaryService::reparentAllChildData()
|
||||
{
|
||||
if (numChildren() <= 0)
|
||||
return;
|
||||
|
||||
std::vector<shared_ptr<Aya::Instance>> children;
|
||||
|
||||
children.insert(children.begin(), getChildren()->begin(), getChildren()->end());
|
||||
|
||||
for (std::vector<shared_ptr<Aya::Instance>>::const_iterator iter = children.begin(); iter != children.end(); ++iter)
|
||||
reparentChildData(*iter);
|
||||
}
|
||||
|
||||
void CSGDictionaryService::reparentChildData(shared_ptr<Aya::Instance> childInstance)
|
||||
{
|
||||
if (!isChildData(childInstance))
|
||||
return;
|
||||
|
||||
NonReplicatedCSGDictionaryService* nrDictionaryService = ServiceProvider::create<NonReplicatedCSGDictionaryService>(DataModel::get(this));
|
||||
childInstance->setParent(nrDictionaryService);
|
||||
|
||||
if (shared_ptr<Aya::BinaryStringValue> bStrValue = Aya::Instance::fastSharedDynamicCast<Aya::BinaryStringValue>(childInstance))
|
||||
{
|
||||
std::string key = createHashKey(bStrValue->getValue().value());
|
||||
instanceMap.erase(key);
|
||||
}
|
||||
}
|
||||
|
||||
void CSGDictionaryService::storeData(PartOperation& partOperation, bool forceIncrement)
|
||||
{
|
||||
BinaryString tmpString;
|
||||
|
||||
tmpString = partOperation.getMeshData();
|
||||
if (FFlag::IgnoreBlankDataOnStore && tmpString.value().size() > 0)
|
||||
{
|
||||
storeStringData(tmpString, forceIncrement, "MeshData");
|
||||
partOperation.setMeshData(tmpString);
|
||||
}
|
||||
|
||||
tmpString = partOperation.getPhysicsData();
|
||||
if (FFlag::IgnoreBlankDataOnStore && tmpString.value().size() > 0)
|
||||
{
|
||||
storeStringData(tmpString, forceIncrement, "PhysicsData");
|
||||
}
|
||||
partOperation.setPhysicsData(tmpString);
|
||||
}
|
||||
|
||||
void CSGDictionaryService::retrieveData(PartOperation& partOperation)
|
||||
{
|
||||
BinaryString tmpString;
|
||||
|
||||
tmpString = partOperation.getMeshData();
|
||||
retrieveStringData(tmpString);
|
||||
partOperation.setMeshData(tmpString);
|
||||
|
||||
tmpString = partOperation.getPhysicsData();
|
||||
retrieveStringData(tmpString);
|
||||
partOperation.setPhysicsData(tmpString);
|
||||
}
|
||||
|
||||
void CSGDictionaryService::storeAllDescendants(shared_ptr<Instance> instance)
|
||||
{
|
||||
if (instance->getChildren())
|
||||
for (Aya::Instances::const_iterator iter = instance->getChildren()->begin(); iter != instance->getChildren()->end(); ++iter)
|
||||
storeAllDescendants(*iter);
|
||||
|
||||
if (shared_ptr<PartOperation> childOperation = Aya::Instance::fastSharedDynamicCast<PartOperation>(instance))
|
||||
storeData(*childOperation);
|
||||
}
|
||||
|
||||
void CSGDictionaryService::retrieveAllDescendants(shared_ptr<Instance> instance)
|
||||
{
|
||||
if (instance->getChildren())
|
||||
for (Aya::Instances::const_iterator iter = instance->getChildren()->begin(); iter != instance->getChildren()->end(); ++iter)
|
||||
retrieveAllDescendants(*iter);
|
||||
|
||||
if (shared_ptr<PartOperation> childOperation = Aya::Instance::fastSharedDynamicCast<PartOperation>(instance))
|
||||
retrieveData(*childOperation);
|
||||
}
|
||||
|
||||
void CSGDictionaryService::refreshRefCountUnderInstance(Aya::Instance* instance)
|
||||
{
|
||||
if (Aya::PartOperation* partOperation = Aya::Instance::fastDynamicCast<Aya::PartOperation>(instance))
|
||||
storeData(*partOperation, true);
|
||||
|
||||
if (instance->getChildren())
|
||||
for (Aya::Instances::const_iterator iter = instance->getChildren()->begin(); iter != instance->getChildren()->end(); ++iter)
|
||||
refreshRefCountUnderInstance(iter->get());
|
||||
}
|
||||
|
||||
boost::shared_ptr<CSGMesh> CSGDictionaryService::insertMesh(const std::string key, const Aya::BinaryString& meshData)
|
||||
{
|
||||
shared_ptr<CSGMesh> mesh = shared_ptr<CSGMesh>(CSGMeshFactory::singleton()->createMesh());
|
||||
mesh->fromBinaryString(meshData.value());
|
||||
cachedMeshMap.insert(std::make_pair(key, mesh));
|
||||
return cachedMeshMap.at(key);
|
||||
}
|
||||
|
||||
void CSGDictionaryService::insertMesh(PartOperation& partOperation)
|
||||
{
|
||||
const BinaryString& keyIn = partOperation.getMeshData();
|
||||
|
||||
if (keyIn.value().size() > minKeySize)
|
||||
{
|
||||
const std::string key = getLocalKeyHash(keyIn);
|
||||
if (strncmp(keyIn.value().c_str(), localKeyTag.c_str(), minKeySize) == 0)
|
||||
{
|
||||
if (!cachedBREPMeshMap.count(key))
|
||||
{
|
||||
shared_ptr<CSGMesh> mesh = partOperation.getMesh();
|
||||
cachedBREPMeshMap.insert(std::make_pair(key, mesh));
|
||||
if (cachedMeshMap.count(key))
|
||||
cachedMeshMap.erase(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boost::shared_ptr<CSGMesh> CSGDictionaryService::getMesh(PartOperation& partOperation)
|
||||
{
|
||||
const BinaryString& keyIn = partOperation.getMeshData();
|
||||
|
||||
if (keyIn.value().size() > minKeySize)
|
||||
{
|
||||
std::string key = getLocalKeyHash(keyIn);
|
||||
|
||||
if (strncmp(keyIn.value().c_str(), localKeyTag.c_str(), minKeySize) == 0)
|
||||
{
|
||||
if (cachedBREPMeshMap.count(key))
|
||||
{
|
||||
return cachedBREPMeshMap.at(key);
|
||||
}
|
||||
else if (cachedMeshMap.count(key))
|
||||
{
|
||||
return cachedMeshMap.at(key);
|
||||
}
|
||||
else if (instanceMap.count(key))
|
||||
{
|
||||
if (shared_ptr<Aya::BinaryStringValue> meshData = instanceMap.at(key).ref.lock())
|
||||
return insertMesh(key, meshData->getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
return boost::shared_ptr<CSGMesh>();
|
||||
}
|
||||
|
||||
void CSGDictionaryService::retrieveMeshData(PartOperation& partOperation)
|
||||
{
|
||||
BinaryString tmpString = partOperation.getMeshData();
|
||||
retrieveStringData(tmpString);
|
||||
partOperation.setMeshData(tmpString);
|
||||
}
|
||||
|
||||
void CSGDictionaryService::storePhysicsData(PartOperation& partOperation, bool forceIncrement)
|
||||
{
|
||||
BinaryString tmpString = partOperation.getPhysicsData();
|
||||
storeStringData(tmpString, forceIncrement, "PhysicsData");
|
||||
partOperation.setPhysicsData(tmpString);
|
||||
}
|
||||
|
||||
void CSGDictionaryService::retrievePhysicsData(PartOperation& partOperation)
|
||||
{
|
||||
BinaryString tmpString = partOperation.getPhysicsData();
|
||||
retrieveStringData(tmpString);
|
||||
partOperation.setPhysicsData(tmpString);
|
||||
}
|
||||
|
||||
void CSGDictionaryService::onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
|
||||
{
|
||||
FlyweightService::onServiceProvider(oldProvider, newProvider);
|
||||
|
||||
if (FFlag::CSGLoadFromCDN && !oldProvider && newProvider)
|
||||
{
|
||||
DataModel* dataModel = DataModel::get(this);
|
||||
|
||||
if (!dataModel)
|
||||
return;
|
||||
|
||||
dataModel->workspaceLoadedSignal.connect(boost::bind(&CSGDictionaryService::onWorkspaceLoaded, shared_from(this)));
|
||||
}
|
||||
}
|
||||
|
||||
void findAndStripMeshData(std::set<BinaryString>* keys, shared_ptr<Instance> descendant)
|
||||
{
|
||||
if (shared_ptr<PartOperation> partOperation = Instance::fastSharedDynamicCast<PartOperation>(descendant))
|
||||
{
|
||||
if (partOperation->hasAsset() && partOperation->getMeshData().value().size() > 0)
|
||||
{
|
||||
keys->insert(partOperation->getMeshData());
|
||||
partOperation->setMeshData(BinaryString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CSGDictionaryService::onWorkspaceLoaded()
|
||||
{
|
||||
std::set<BinaryString> keys;
|
||||
|
||||
DataModel* dataModel = DataModel::get(this);
|
||||
|
||||
if (!dataModel)
|
||||
return;
|
||||
|
||||
dataModel->visitDescendants(boost::bind(&findAndStripMeshData, &keys, _1));
|
||||
|
||||
for (std::set<BinaryString>::iterator iter = keys.begin(); iter != keys.end(); ++iter)
|
||||
{
|
||||
removeStringData((*iter));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
64
engine/app/src/DataModel/CSGDictionaryService.hpp
Normal file
64
engine/app/src/DataModel/CSGDictionaryService.hpp
Normal file
@@ -0,0 +1,64 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Utility/BinaryString.hpp"
|
||||
#include "Tree/Instance.hpp"
|
||||
#include "Tree/Service.hpp"
|
||||
#include "DataModel/FlyweightService.hpp"
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include "Value.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
class PartOperation;
|
||||
class CSGMesh;
|
||||
|
||||
extern const char* const sCSGDictionaryService;
|
||||
|
||||
class CSGDictionaryService
|
||||
: public DescribedCreatable<CSGDictionaryService, FlyweightService, sCSGDictionaryService, Reflection::ClassDescriptor::PERSISTENT,
|
||||
Security::Roblox>
|
||||
{
|
||||
protected:
|
||||
typedef DescribedCreatable<CSGDictionaryService, FlyweightService, sCSGDictionaryService, Reflection::ClassDescriptor::PERSISTENT,
|
||||
Security::Roblox>
|
||||
Super;
|
||||
typedef boost::unordered_map<std::string, boost::shared_ptr<CSGMesh>> CSGMeshMap;
|
||||
CSGMeshMap cachedBREPMeshMap;
|
||||
CSGMeshMap cachedMeshMap;
|
||||
|
||||
void reparentChildData(shared_ptr<Aya::Instance> sharedInstance);
|
||||
|
||||
virtual void refreshRefCountUnderInstance(Aya::Instance* instance);
|
||||
|
||||
boost::shared_ptr<CSGMesh> insertMesh(const std::string key, const Aya::BinaryString& meshData);
|
||||
|
||||
boost::shared_ptr<CSGMesh> insertCachedMesh(const std::string key, const Aya::BinaryString& meshData);
|
||||
|
||||
virtual void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
|
||||
|
||||
public:
|
||||
CSGDictionaryService();
|
||||
|
||||
void storeData(PartOperation& partOperation, bool forceIncrement = false);
|
||||
void retrieveData(PartOperation& partOperation);
|
||||
|
||||
void storeAllDescendants(shared_ptr<Aya::Instance> instance);
|
||||
void retrieveAllDescendants(shared_ptr<Aya::Instance> instance);
|
||||
|
||||
void reparentAllChildData();
|
||||
|
||||
void insertMesh(PartOperation& partOperation);
|
||||
boost::shared_ptr<CSGMesh> getMesh(PartOperation& partOperation);
|
||||
|
||||
boost::shared_ptr<CSGMesh> getCachedMesh(PartOperation& partOperation);
|
||||
|
||||
void retrieveMeshData(PartOperation& partOperation);
|
||||
|
||||
void storePhysicsData(PartOperation& partOperation, bool forceIncrement = false);
|
||||
void retrievePhysicsData(PartOperation& partOperation);
|
||||
|
||||
void onWorkspaceLoaded();
|
||||
};
|
||||
} // namespace Aya
|
||||
348
engine/app/src/DataModel/CSGMesh.cpp
Normal file
348
engine/app/src/DataModel/CSGMesh.cpp
Normal file
@@ -0,0 +1,348 @@
|
||||
|
||||
|
||||
|
||||
#include "DataModel/CSGMesh.hpp"
|
||||
|
||||
#include "World/TriangleMesh.hpp"
|
||||
|
||||
#include "Utility/Lcmrand.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <streambuf>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
#include "Utility/MD5Hasher.hpp"
|
||||
|
||||
FASTFLAGVARIABLE(FixGlowingCSG, true)
|
||||
|
||||
using namespace G3D;
|
||||
|
||||
#ifndef CSG_KERNEL_OLD
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
CSGMeshFactory* csgMeshFactory = 0;
|
||||
}
|
||||
|
||||
void CSGMeshFactory::set(CSGMeshFactory* factory)
|
||||
{
|
||||
csgMeshFactory = factory;
|
||||
}
|
||||
|
||||
CSGMeshFactory* CSGMeshFactory::singleton()
|
||||
{
|
||||
if (csgMeshFactory)
|
||||
return csgMeshFactory;
|
||||
|
||||
static CSGMeshFactory* meshFactory = new CSGMeshFactory;
|
||||
return meshFactory;
|
||||
}
|
||||
|
||||
CSGMesh* CSGMeshFactory::createMesh()
|
||||
{
|
||||
return new CSGMesh;
|
||||
}
|
||||
|
||||
CSGMesh::CSGMesh()
|
||||
: version(2)
|
||||
, brepVersion(1)
|
||||
, badMesh(false)
|
||||
{
|
||||
}
|
||||
|
||||
CSGMesh::~CSGMesh() {}
|
||||
|
||||
void CSGMesh::clearMesh()
|
||||
{
|
||||
vertices.clear();
|
||||
indices.clear();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// The below code is to provide a first line of defense against expert users
|
||||
// injecting random data or their own data into the geometry stream.
|
||||
// It is not expected to completely prevent attempts at injection but
|
||||
// it will slow down the process so that we can move the generation of
|
||||
// the mesh to a service on a server.
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
void generateRandomString(char* s, const size_t len)
|
||||
{
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
{
|
||||
s[i] = (char)rand();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const size_t saltSize = 16;
|
||||
const size_t hashSize = 16;
|
||||
|
||||
std::string CSGMesh::createHash(const std::string saltIn) const
|
||||
{
|
||||
const size_t verticesSize = vertices.size() * sizeof(CSGVertex);
|
||||
const size_t indicesSize = indices.size() * sizeof(unsigned int);
|
||||
size_t buffSize = verticesSize + indicesSize + saltSize;
|
||||
|
||||
std::vector<unsigned char> byteBuffer(buffSize);
|
||||
std::vector<char> hash(saltSize + hashSize);
|
||||
|
||||
std::string salt = saltIn;
|
||||
|
||||
if (salt.empty())
|
||||
{
|
||||
salt.resize(saltSize);
|
||||
generateRandomString(&salt[0], salt.size());
|
||||
}
|
||||
|
||||
size_t copyOffset = 0;
|
||||
memcpy(&byteBuffer[copyOffset], &vertices[0], verticesSize);
|
||||
|
||||
copyOffset += verticesSize;
|
||||
memcpy(&byteBuffer[copyOffset], &indices[0], indicesSize);
|
||||
|
||||
copyOffset += indicesSize;
|
||||
memcpy(&byteBuffer[copyOffset], salt.c_str(), salt.size());
|
||||
|
||||
LcmRand randGen;
|
||||
for (size_t i = 0; i < buffSize; i++)
|
||||
{
|
||||
std::swap(byteBuffer[i], byteBuffer[randGen.value() % buffSize]);
|
||||
}
|
||||
|
||||
boost::scoped_ptr<Aya::MD5Hasher> hasher(Aya::MD5Hasher::create());
|
||||
hasher->addData((const char*)&byteBuffer[0], byteBuffer.size());
|
||||
|
||||
memcpy(&hash[0], hasher->toString().c_str(), hashSize);
|
||||
memcpy(&hash[hashSize], salt.c_str(), saltSize);
|
||||
|
||||
std::string hashStr(&hash[0], hashSize + saltSize);
|
||||
|
||||
return hashStr;
|
||||
}
|
||||
|
||||
void CSGMesh::computeDecalRemap()
|
||||
{
|
||||
if (!FFlag::FixGlowingCSG)
|
||||
return;
|
||||
|
||||
for (unsigned i = 0; i < 6; ++i)
|
||||
{
|
||||
decalVertexRemap[i].clear();
|
||||
decalIndexRemap[i].clear();
|
||||
}
|
||||
|
||||
std::vector<unsigned> tmpTranslation;
|
||||
tmpTranslation.resize(vertices.size());
|
||||
|
||||
for (unsigned vi = 0; vi < vertices.size(); ++vi)
|
||||
{
|
||||
unsigned face = vertices[vi].extra.r - 1;
|
||||
AYAASSERT(face < 6);
|
||||
decalVertexRemap[face].push_back(vi);
|
||||
tmpTranslation[vi] = decalVertexRemap[face].size() - 1;
|
||||
}
|
||||
|
||||
for (unsigned ii = 0; ii < indices.size(); ++ii)
|
||||
{
|
||||
unsigned face = vertices[indices[ii]].extra.r - 1;
|
||||
decalIndexRemap[face].push_back(tmpTranslation[indices[ii]]);
|
||||
}
|
||||
}
|
||||
|
||||
void xorBuffer(std::string& buffer)
|
||||
{
|
||||
const size_t basicEncryptionKeySize = 31;
|
||||
|
||||
LcmRand randGen;
|
||||
std::string basicEncryptionKey;
|
||||
basicEncryptionKey.resize(basicEncryptionKeySize);
|
||||
|
||||
for (size_t i = 0; i < basicEncryptionKey.size(); i++)
|
||||
basicEncryptionKey[i] = randGen.value() % CHAR_MAX;
|
||||
|
||||
for (size_t i = 0; i < buffer.size(); i++)
|
||||
buffer[i] = buffer[i] ^ basicEncryptionKey[i % basicEncryptionKey.size()];
|
||||
}
|
||||
|
||||
std::string headerTag("CSGMDL");
|
||||
|
||||
std::string CSGMesh::toBinaryString() const
|
||||
{
|
||||
std::stringstream stream;
|
||||
|
||||
stream.write(headerTag.c_str(), headerTag.size());
|
||||
|
||||
stream.write(reinterpret_cast<const char*>(&version), sizeof(version));
|
||||
|
||||
std::string hash = createHash();
|
||||
|
||||
stream.write(reinterpret_cast<const char*>(hash.c_str()), hashSize + saltSize);
|
||||
|
||||
unsigned int numVertices = vertices.size();
|
||||
unsigned int vertexStride = sizeof(CSGVertex);
|
||||
stream.write(reinterpret_cast<const char*>(&numVertices), sizeof(unsigned int));
|
||||
stream.write(reinterpret_cast<const char*>(&vertexStride), sizeof(unsigned int));
|
||||
stream.write(reinterpret_cast<const char*>(&vertices[0]), vertexStride * numVertices);
|
||||
|
||||
unsigned int numIndices = indices.size();
|
||||
stream.write(reinterpret_cast<const char*>(&numIndices), sizeof(unsigned int));
|
||||
stream.write(reinterpret_cast<const char*>(&indices[0]), sizeof(unsigned int) * numIndices);
|
||||
|
||||
std::string buffer(stream.str().c_str(), stream.str().size());
|
||||
|
||||
xorBuffer(buffer);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::string CSGMesh::toBinaryStringForPhysics() const
|
||||
{
|
||||
std::vector<btVector3> vertexPositions;
|
||||
for (unsigned int i = 0; i < vertices.size(); i++)
|
||||
vertexPositions.push_back(btVector3(vertices[i].pos.x, vertices[i].pos.y, vertices[i].pos.z));
|
||||
|
||||
return TriangleMesh::generateStaticMeshData(indices, vertexPositions);
|
||||
}
|
||||
|
||||
bool CSGMesh::fromBinaryString(const std::string& str)
|
||||
{
|
||||
std::string buffer(str.c_str(), str.size());
|
||||
|
||||
xorBuffer(buffer);
|
||||
|
||||
std::stringstream stream(buffer);
|
||||
int currentVersion = version;
|
||||
|
||||
std::string fileId;
|
||||
fileId.resize(headerTag.size());
|
||||
stream.read(&fileId[0], headerTag.size());
|
||||
|
||||
if (fileId != headerTag)
|
||||
return false;
|
||||
|
||||
stream.read(reinterpret_cast<char*>(&version), sizeof(int));
|
||||
|
||||
if (version != currentVersion)
|
||||
return false;
|
||||
|
||||
std::string hash;
|
||||
hash.resize(hashSize + saltSize);
|
||||
stream.read(reinterpret_cast<char*>(&hash[0]), hashSize + saltSize);
|
||||
|
||||
unsigned int numVertices = 0;
|
||||
unsigned int vertexStride = 0;
|
||||
stream.read(reinterpret_cast<char*>(&numVertices), sizeof(unsigned int));
|
||||
stream.read(reinterpret_cast<char*>(&vertexStride), sizeof(unsigned int));
|
||||
|
||||
if (vertexStride != sizeof(CSGVertex))
|
||||
return false;
|
||||
|
||||
vertices.resize(numVertices);
|
||||
stream.read(reinterpret_cast<char*>(&vertices[0]), vertexStride * numVertices);
|
||||
|
||||
unsigned int numIndices = 0;
|
||||
stream.read(reinterpret_cast<char*>(&numIndices), sizeof(unsigned int));
|
||||
indices.resize(numIndices);
|
||||
stream.read(reinterpret_cast<char*>(&indices[0]), sizeof(unsigned int) * numIndices);
|
||||
|
||||
std::string salt(&hash[hashSize], saltSize);
|
||||
|
||||
std::string newHash = createHash(salt);
|
||||
|
||||
if (hash != newHash)
|
||||
badMesh = true;
|
||||
|
||||
computeDecalRemap();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CSGMesh::set(const std::vector<CSGVertex>& verticesIn, const std::vector<unsigned int>& indicesIn)
|
||||
{
|
||||
vertices = verticesIn;
|
||||
indices = indicesIn;
|
||||
}
|
||||
|
||||
CSGMesh* CSGMesh::clone() const
|
||||
{
|
||||
CSGMesh* mesh = new CSGMesh(*this);
|
||||
return mesh;
|
||||
}
|
||||
|
||||
void CSGVertex::generateUv()
|
||||
{
|
||||
switch (extra.r)
|
||||
{
|
||||
case CSGVertex::UV_BOX_X:
|
||||
uv = Vector2(-pos.z, -pos.y);
|
||||
break;
|
||||
case CSGVertex::UV_BOX_X_NEG:
|
||||
uv = Vector2(pos.z, -pos.y);
|
||||
break;
|
||||
case CSGVertex::UV_BOX_Y:
|
||||
uv = Vector2(-pos.x, -pos.z);
|
||||
break;
|
||||
case CSGVertex::UV_BOX_Y_NEG:
|
||||
uv = Vector2(pos.x, -pos.z);
|
||||
break;
|
||||
case CSGVertex::UV_BOX_Z:
|
||||
uv = Vector2(pos.x, -pos.y);
|
||||
break;
|
||||
case CSGVertex::UV_BOX_Z_NEG:
|
||||
uv = Vector2(-pos.x, -pos.y);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 CSGVertex::generateUv(const Vector3& posIn) const
|
||||
{
|
||||
Vector2 uvResult;
|
||||
|
||||
switch (extra.r)
|
||||
{
|
||||
case CSGVertex::NO_UV_GENERATION:
|
||||
uvResult = uv;
|
||||
break;
|
||||
case CSGVertex::UV_BOX_X:
|
||||
uvResult = Vector2(-posIn.z, -posIn.y);
|
||||
break;
|
||||
case CSGVertex::UV_BOX_X_NEG:
|
||||
uvResult = Vector2(posIn.z, -posIn.y);
|
||||
break;
|
||||
case CSGVertex::UV_BOX_Y:
|
||||
uvResult = Vector2(-posIn.x, -posIn.z);
|
||||
break;
|
||||
case CSGVertex::UV_BOX_Y_NEG:
|
||||
uvResult = Vector2(posIn.x, -posIn.z);
|
||||
break;
|
||||
case CSGVertex::UV_BOX_Z:
|
||||
uvResult = Vector2(posIn.x, -posIn.y);
|
||||
break;
|
||||
case CSGVertex::UV_BOX_Z_NEG:
|
||||
uvResult = Vector2(-posIn.x, -posIn.y);
|
||||
break;
|
||||
}
|
||||
return uvResult;
|
||||
}
|
||||
|
||||
bool CSGMesh::isNotEmpty() const
|
||||
{
|
||||
if ((getVertices().size() > 0) && (getIndices().size() > 0))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
|
||||
#endif
|
||||
179
engine/app/src/DataModel/CSGMesh.hpp
Normal file
179
engine/app/src/DataModel/CSGMesh.hpp
Normal file
@@ -0,0 +1,179 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "g3dmath.hpp"
|
||||
|
||||
#include "Vector2.hpp"
|
||||
#include "Vector3.hpp"
|
||||
#include "Vector4.hpp"
|
||||
#include "Color4uint8.hpp"
|
||||
#include "Color3uint8.hpp"
|
||||
#include "CoordinateFrame.hpp"
|
||||
|
||||
#include "Debug.hpp"
|
||||
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class CSGVertex
|
||||
{
|
||||
public:
|
||||
G3D::Vector3 pos;
|
||||
G3D::Vector3 normal;
|
||||
G3D::Color4uint8 color;
|
||||
G3D::Color4uint8 extra; // red = uv generation type
|
||||
G3D::Vector2 uv;
|
||||
G3D::Vector2 uvStuds;
|
||||
G3D::Vector2 uvDecal;
|
||||
|
||||
G3D::Vector3 tangent;
|
||||
G3D::Vector4 edgeDistances;
|
||||
|
||||
enum UVGenerationType
|
||||
{
|
||||
NO_UV_GENERATION = 0,
|
||||
UV_BOX_X,
|
||||
UV_BOX_Y,
|
||||
UV_BOX_Z,
|
||||
UV_BOX_X_NEG,
|
||||
UV_BOX_Y_NEG,
|
||||
UV_BOX_Z_NEG
|
||||
};
|
||||
|
||||
|
||||
CSGVertex()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
G3D::Vector2 generateUv(const Vector3& pos) const;
|
||||
void generateUv();
|
||||
};
|
||||
|
||||
class CSGMesh
|
||||
{
|
||||
public:
|
||||
CSGMesh();
|
||||
virtual ~CSGMesh();
|
||||
|
||||
virtual CSGMesh* clone() const;
|
||||
|
||||
const std::vector<CSGVertex>& getVertices() const
|
||||
{
|
||||
return vertices;
|
||||
}
|
||||
const std::vector<unsigned int>& getIndices() const
|
||||
{
|
||||
return indices;
|
||||
}
|
||||
|
||||
const std::vector<unsigned>& getIndexRemap(unsigned idx) const
|
||||
{
|
||||
AYAASSERT(idx < 6);
|
||||
return decalIndexRemap[idx];
|
||||
}
|
||||
const std::vector<unsigned>& getVertexRemap(unsigned idx) const
|
||||
{
|
||||
AYAASSERT(idx < 6);
|
||||
return decalVertexRemap[idx];
|
||||
}
|
||||
|
||||
std::string createHash(const std::string salt = "") const;
|
||||
|
||||
bool isBadMesh() const
|
||||
{
|
||||
return badMesh;
|
||||
}
|
||||
virtual bool isValid() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void set(const std::vector<CSGVertex>& vertices, const std::vector<unsigned int>& indices);
|
||||
void clearMesh();
|
||||
|
||||
virtual void translate(const G3D::Vector3& translation) {}
|
||||
|
||||
virtual void applyCoordinateFrame(G3D::CoordinateFrame cFrame) {}
|
||||
virtual void applyTranslation(const G3D::Vector3& trans) {}
|
||||
virtual void applyColor(const G3D::Vector3& color) {}
|
||||
virtual void applyScale(const G3D::Vector3& size) {}
|
||||
virtual void triangulate() {}
|
||||
virtual bool newTriangulate()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
virtual void weldMesh(bool positionOnly = false) {}
|
||||
|
||||
virtual void buildBRep() {}
|
||||
|
||||
virtual bool unionMesh(const CSGMesh* a, const CSGMesh* b)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool intersectMesh(const CSGMesh* a, const CSGMesh* b)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool subractMesh(const CSGMesh* a, const CSGMesh* b)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isNotEmpty() const;
|
||||
|
||||
std::string toBinaryString() const;
|
||||
std::string toBinaryStringForPhysics() const;
|
||||
bool fromBinaryString(const std::string& str);
|
||||
virtual std::string getBRepBinaryString() const
|
||||
{
|
||||
return "";
|
||||
}
|
||||
virtual void setBRepFromBinaryString(const std::string& str) {}
|
||||
|
||||
virtual size_t clusterVertices(float resolution)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
virtual bool makeHalfEdges(std::vector<int>& vertexEdges)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual G3D::Vector3 extentsCenter()
|
||||
{
|
||||
G3D::Vector3 v;
|
||||
return v;
|
||||
}
|
||||
virtual G3D::Vector3 extentsSize()
|
||||
{
|
||||
G3D::Vector3 v;
|
||||
return v;
|
||||
}
|
||||
|
||||
void computeDecalRemap();
|
||||
|
||||
protected:
|
||||
int version;
|
||||
int brepVersion;
|
||||
bool badMesh;
|
||||
|
||||
std::vector<CSGVertex> vertices;
|
||||
std::vector<unsigned int> indices;
|
||||
|
||||
std::vector<unsigned> decalVertexRemap[6];
|
||||
std::vector<unsigned> decalIndexRemap[6];
|
||||
};
|
||||
|
||||
class CSGMeshFactory
|
||||
{
|
||||
public:
|
||||
virtual CSGMesh* createMesh();
|
||||
|
||||
static CSGMeshFactory* singleton();
|
||||
static void set(CSGMeshFactory* factory);
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
98
engine/app/src/DataModel/CacheableContentProvider.hpp
Normal file
98
engine/app/src/DataModel/CacheableContentProvider.hpp
Normal file
@@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "Tree/Service.hpp"
|
||||
#include "DataModel/ContentProvider.hpp"
|
||||
#include "Utility/ContentProviderJob.hpp"
|
||||
#include "Utility/ControlledLRUCache.hpp"
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/unordered_set.hpp>
|
||||
|
||||
typedef boost::unordered_set<std::string> Set;
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
extern const char* const sCacheableContentProvider;
|
||||
class CacheableContentProvider
|
||||
: public DescribedNonCreatable<CacheableContentProvider, Instance, sCacheableContentProvider, Aya::Reflection::ClassDescriptor::RUNTIME_LOCAL>
|
||||
, public Service
|
||||
, public HeartbeatInstance
|
||||
{
|
||||
typedef DescribedNonCreatable<CacheableContentProvider, Instance, sCacheableContentProvider, Aya::Reflection::ClassDescriptor::RUNTIME_LOCAL>
|
||||
Super;
|
||||
|
||||
protected:
|
||||
class CachedItem
|
||||
{
|
||||
public:
|
||||
AsyncHttpQueue::RequestResult requestResult;
|
||||
shared_ptr<void> data;
|
||||
|
||||
public:
|
||||
CachedItem(shared_ptr<void> data = shared_ptr<void>(), AsyncHttpQueue::RequestResult result = AsyncHttpQueue::Waiting)
|
||||
: data(data)
|
||||
, requestResult(result)
|
||||
{
|
||||
}
|
||||
~CachedItem()
|
||||
{
|
||||
data.reset();
|
||||
}
|
||||
friend class CacheableContentProvider;
|
||||
};
|
||||
|
||||
shared_ptr<ContentProviderJob> contentJob;
|
||||
Aya::atomic<int> pendingRequests;
|
||||
bool immediateMode;
|
||||
|
||||
boost::scoped_ptr<ConcurrentControlledLRUCache<std::string, boost::shared_ptr<CachedItem>>> lruCache;
|
||||
|
||||
boost::mutex failedCacheMutex;
|
||||
Set failedCache;
|
||||
|
||||
public:
|
||||
CacheableContentProvider(CacheSizeEnforceMethod enforceMethod, unsigned long size);
|
||||
virtual ~CacheableContentProvider();
|
||||
|
||||
void setCacheSize(int size);
|
||||
void setImmediateMode();
|
||||
bool isRequestQueueEmpty()
|
||||
{
|
||||
return pendingRequests == 0;
|
||||
}
|
||||
|
||||
bool clearContent();
|
||||
|
||||
bool hasContent(const ContentId& id);
|
||||
boost::shared_ptr<void> requestContent(const ContentId& id, float priority, bool markUsed, AsyncHttpQueue::RequestResult& result);
|
||||
boost::shared_ptr<void> blockingRequestContent(const ContentId& id, bool markUsed);
|
||||
boost::shared_ptr<void> fetchContent(const ContentId& id);
|
||||
|
||||
bool isAssetFailed(const ContentId& id);
|
||||
|
||||
// HeartbeatInstance
|
||||
/*override*/ void onHeartbeat(const Heartbeat& event);
|
||||
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
|
||||
|
||||
protected:
|
||||
static void LoadContentCallbackHelper(boost::weak_ptr<CacheableContentProvider> cacheableContentProvider, AsyncHttpQueue::RequestResult result,
|
||||
std::istream* filestream, shared_ptr<const std::string> data, std::string id);
|
||||
void LoadContentCallback(AsyncHttpQueue::RequestResult result, std::istream* filestream, shared_ptr<const std::string> data, std::string id);
|
||||
|
||||
static TaskScheduler::StepResult ProcessTaskHelper(
|
||||
boost::weak_ptr<CacheableContentProvider> weakCcp, const std::string& id, shared_ptr<const std::string> data);
|
||||
virtual TaskScheduler::StepResult ProcessTask(const std::string& id, shared_ptr<const std::string> data) = 0;
|
||||
|
||||
static void ErrorTaskHelper(boost::weak_ptr<CacheableContentProvider> weakCcp, const std::string& id);
|
||||
virtual void ErrorTask(const std::string& id);
|
||||
|
||||
bool isAssetContent(ContentId id);
|
||||
void markContentFailed(const std::string& id);
|
||||
AsyncHttpQueue::RequestResult getContentStatus(const std::string& id);
|
||||
|
||||
virtual void updateContent(const std::string& id, boost::shared_ptr<CachedItem> cachedItem);
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
1622
engine/app/src/DataModel/Camera.cpp
Normal file
1622
engine/app/src/DataModel/Camera.cpp
Normal file
File diff suppressed because it is too large
Load Diff
382
engine/app/src/DataModel/Camera.hpp
Normal file
382
engine/app/src/DataModel/Camera.hpp
Normal file
@@ -0,0 +1,382 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Tree/Instance.hpp"
|
||||
#include "Utility/G3DCore.hpp"
|
||||
#include "Utility/HeartbeatInstance.hpp"
|
||||
#include <vector>
|
||||
#include "Frustum.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class ICameraOwner;
|
||||
class CameraSubject;
|
||||
class ContactManager;
|
||||
class Primitive;
|
||||
class NavKeys;
|
||||
class Extents;
|
||||
class ModelInstance;
|
||||
class PartInstance;
|
||||
class HitTestFilter;
|
||||
|
||||
extern const char* const sCamera;
|
||||
class Camera
|
||||
: public DescribedCreatable<Camera, Instance, sCamera, Reflection::ClassDescriptor::PERSISTENT_LOCAL>
|
||||
, public HeartbeatInstance
|
||||
{
|
||||
public:
|
||||
Aya::signal<void()> interpolationFinishedSignal;
|
||||
Aya::signal<void(bool)> firstPersonTransitionSignal;
|
||||
Aya::signal<void(CoordinateFrame)> cframeChangedSignal;
|
||||
|
||||
// Warning - these enums are part of the XML - only append
|
||||
enum CameraType
|
||||
{
|
||||
FIXED_CAMERA = 0,
|
||||
ATTACH_CAMERA = 1, // maintain fixed position and rotation w.r.t target coordinate frame
|
||||
WATCH_CAMERA = 2, // rotate to keep target in view
|
||||
TRACK_CAMERA = 3, // translate to keep target in view
|
||||
FOLLOW_CAMERA = 4, // rotate and translate to keep target in view
|
||||
CUSTOM_CAMERA = 5,
|
||||
LOCKED_CAMERA = 6,
|
||||
NUM_CAMERA_TYPE = 7
|
||||
};
|
||||
typedef enum
|
||||
{
|
||||
ZOOM_IN_OR_OUT,
|
||||
ZOOM_OUT_ONLY
|
||||
} ZoomType;
|
||||
enum CameraMode
|
||||
{
|
||||
CAMERAMODE_CLASSIC = 0,
|
||||
CAMERAMODE_LOCKFIRSTPERSON = 1
|
||||
};
|
||||
enum CameraPanMode
|
||||
{
|
||||
CAMERAPANMODE_CLASSIC = 0,
|
||||
CAMERAPANMODE_EDGEBUMP = 1,
|
||||
};
|
||||
|
||||
|
||||
static float distanceDefault()
|
||||
{
|
||||
return 36.0f;
|
||||
} // grids
|
||||
static float distanceMin()
|
||||
{
|
||||
return 0.5f;
|
||||
} // down from 4.0
|
||||
static float distanceMax()
|
||||
{
|
||||
return 1000.0f;
|
||||
}
|
||||
static float distanceMaxCharacter()
|
||||
{
|
||||
return 400.0f;
|
||||
}
|
||||
static float distanceMinOcclude()
|
||||
{
|
||||
return 4.0f;
|
||||
}
|
||||
|
||||
static double interpolationSpeed()
|
||||
{
|
||||
return 5.0f;
|
||||
} // studs per second
|
||||
|
||||
static bool legalCameraCoord(const CoordinateFrame& c);
|
||||
|
||||
static float CameraKeyMoveFactor;
|
||||
static float CameraMouseWheelMoveFactor;
|
||||
static float CameraShiftKeyMoveFactor;
|
||||
|
||||
Camera();
|
||||
~Camera() {}
|
||||
|
||||
// Yuck - called by Window code
|
||||
void step(double elapsedTime);
|
||||
void stepSubject();
|
||||
|
||||
void pushCameraHistoryStack();
|
||||
std::pair<CoordinateFrame, CoordinateFrame> popCameraHistoryStack(bool backward);
|
||||
|
||||
void getHeadingElevationDistance(float& heading, float& elevation, float& distance);
|
||||
|
||||
bool isCharacterCamera() const;
|
||||
bool isFirstPersonCamera() const; // hack for now - this should be in CameraSubject?? discuss
|
||||
|
||||
bool isPartInFrustum(const PartInstance& part) const;
|
||||
bool isPartVisibleFast(const PartInstance& part, const ContactManager& contactManager,
|
||||
const HitTestFilter* filter = NULL) const; // uses only one ray to do check (can have inaccurate results, but takes 1/4 the time)
|
||||
|
||||
bool isLockedToFirstPerson() const;
|
||||
|
||||
CameraSubject* getCameraSubject();
|
||||
const CameraSubject* getConstCameraSubject() const;
|
||||
Instance* getCameraSubjectInstanceDangerous() const; // for reflection only
|
||||
void setCameraSubject(Instance* newSubject);
|
||||
|
||||
void setCameraLerpGoals(const CoordinateFrame& cameraCoordValue, const CoordinateFrame& cameraFocusValue);
|
||||
|
||||
const CoordinateFrame& getCameraFocus() const
|
||||
{
|
||||
return cameraFocus;
|
||||
}
|
||||
void setCameraFocus(const CoordinateFrame& value); // sets camera focus and camera focus goal
|
||||
void setCameraFocusWithoutPropertyChange(const CoordinateFrame& value);
|
||||
void setCameraFocusOnly(const CoordinateFrame& value); // sets camera focus
|
||||
void setCameraFocusOnlyWithoutPropertyChange(const CoordinateFrame& value);
|
||||
void setCameraFocusAndMaintainFocus(const CoordinateFrame& value, bool maintainFocusOnPoint);
|
||||
|
||||
const CoordinateFrame& getCameraCoordinateFrame() const
|
||||
{
|
||||
return cameraCoord;
|
||||
}
|
||||
void setCameraCoordinateFrame(const CoordinateFrame& value);
|
||||
|
||||
CoordinateFrame getRenderingCoordinateFrame() const;
|
||||
CoordinateFrame getRenderingCoordinateFrameLua()
|
||||
{
|
||||
return getRenderingCoordinateFrame();
|
||||
}
|
||||
|
||||
bool getHeadLocked() const
|
||||
{
|
||||
return headLocked;
|
||||
}
|
||||
void setHeadLocked(bool value);
|
||||
|
||||
CameraType getCameraType() const
|
||||
{
|
||||
return cameraType;
|
||||
}
|
||||
void setCameraType(CameraType value);
|
||||
|
||||
bool canZoom(bool inwards) const;
|
||||
bool canTilt(int up) const;
|
||||
|
||||
// Cursor Control
|
||||
void onMousePan(const Vector2& wrapMouseDelta);
|
||||
void onMouseTrack(const Vector2& wrapMouseDelta);
|
||||
|
||||
// Zoom
|
||||
bool zoom(float in);
|
||||
bool setDistanceFromTarget(float newDistance);
|
||||
bool setDistanceFromTarget(float newDistance, CoordinateFrame& newCameraPos, const CoordinateFrame& newCameraFocus);
|
||||
|
||||
bool zoomExtents(); // of the world
|
||||
void zoomExtents(const ModelInstance* model, ZoomType zoomType);
|
||||
void zoomExtents(const Extents& extents, ZoomType zoomType);
|
||||
|
||||
void lerpToExtents(const Extents& extents);
|
||||
|
||||
// Pan
|
||||
void panRadians(float angle);
|
||||
void panUnits(int units);
|
||||
void panSpeedRadians(float tilt);
|
||||
float getPanSpeed(void)
|
||||
{
|
||||
return panSpeed;
|
||||
}
|
||||
|
||||
// Tilt
|
||||
bool tiltRadians(float up);
|
||||
bool tiltUnits(int up);
|
||||
void tiltSpeedRadians(float tilt);
|
||||
float getTiltSpeed(void)
|
||||
{
|
||||
return tiltSpeed;
|
||||
}
|
||||
|
||||
//
|
||||
void setCameraPanMode(Camera::CameraPanMode mode);
|
||||
Camera::CameraPanMode getCameraPanMode() const
|
||||
{
|
||||
return cameraPanMode;
|
||||
}
|
||||
|
||||
// LookAt
|
||||
void lookAt(const Vector3& point, bool lerpCamera);
|
||||
void setImageServerViewNoLerp(const CoordinateFrame& modelCoord);
|
||||
|
||||
// Fly
|
||||
void doFly(const NavKeys& nav, int steps);
|
||||
|
||||
// Camera History
|
||||
void stepCameraHistoryForward();
|
||||
void stepCameraHistoryBackward();
|
||||
|
||||
// Util
|
||||
static float getNewZoomDistance(float currentDistance, float in);
|
||||
|
||||
bool hasClientPlayer() const;
|
||||
|
||||
// Clipping plane, *not* imaging plane. Returns a negative z-value.
|
||||
float nearPlaneZ() const;
|
||||
|
||||
// Returns a negative z-value.
|
||||
inline float farPlaneZ() const
|
||||
{
|
||||
return -5e3f;
|
||||
}
|
||||
|
||||
inline float getFieldOfView() const
|
||||
{
|
||||
return fieldOfView;
|
||||
}
|
||||
inline float getFieldOfViewDegrees() const
|
||||
{
|
||||
return G3D::toDegrees(fieldOfView);
|
||||
}
|
||||
void setFieldOfViewDegrees(float value);
|
||||
|
||||
inline float getRoll() const
|
||||
{
|
||||
return roll;
|
||||
}
|
||||
void setRoll(float value);
|
||||
float getRollSlow();
|
||||
|
||||
// Taken from RbxCamera, a derivative of the G3D Camera.
|
||||
// Returns the image plane depth, <I>s'</I>, given the current field
|
||||
// of view for film of dimensions width x height. See
|
||||
// setImagePlaneDepth for a discussion of worldspace values width and height.
|
||||
float getImagePlaneDepth() const;
|
||||
|
||||
// Returns the Camera space width of the viewport.
|
||||
float getViewportWidth() const;
|
||||
|
||||
// Returns the Camera space height of the viewport.
|
||||
float getViewportHeight() const;
|
||||
|
||||
// Taken from RbxCamera, a derivative of the G3D Camera.
|
||||
// Projects a world space point onto a width x height screen. The
|
||||
// returned coordinate uses pixmap addressing: x = right and y =
|
||||
// down. The resulting z value is <I>rhw</I>.
|
||||
// If the point is behind the camera, Vector3::inf() is returned.
|
||||
Vector3 project(const Vector3& point) const;
|
||||
shared_ptr<const Reflection::Tuple> projectLua(Vector3 point);
|
||||
shared_ptr<const Reflection::Tuple> projectViewportLua(Vector3 point);
|
||||
|
||||
// Taken from RbxCamera, a derivative of the G3D Camera.
|
||||
// Returns the world space ray passing through the center of pixel
|
||||
// (x, y) on the image plane. The pixel x and y axes are opposite
|
||||
// the 3D object space axes: (0,0) is the upper left corner of the screen.
|
||||
// They are in viewport coordinates, not screen coordinates.
|
||||
// Integer (x, y) values correspond to
|
||||
// the upper left corners of pixels. If you want to cast rays
|
||||
// through pixel centers, add 0.5 to x and y.
|
||||
RbxRay worldRay(float x, float y, float depth = 0.0f) const;
|
||||
RbxRay worldRayLua(float x, float y, float depth);
|
||||
RbxRay worldRayViewportLua(float x, float y, float depth);
|
||||
|
||||
// Taken from RbxCamera, a derivative of the G3D Camera.
|
||||
// Returns the world space view frustum, which is a truncated pyramid describing
|
||||
// the volume of space seen by this camera.
|
||||
void frustum(const float farPlaneZ, Aya::Frustum& fr) const;
|
||||
|
||||
Aya::Frustum frustum() const;
|
||||
|
||||
const CoordinateFrame& coordinateFrame() const;
|
||||
|
||||
Vector2int16 getViewport() const
|
||||
{
|
||||
return viewport;
|
||||
}
|
||||
void setViewport(Vector2int16 newViewport);
|
||||
|
||||
// Calculates the dot product of camera direction and point. Useful for
|
||||
// half space test or getting angle between the 2
|
||||
float dot(const Vector3& point) const;
|
||||
|
||||
void beginCameraInterpolation(CoordinateFrame endPos, CoordinateFrame endFocus, float duration);
|
||||
|
||||
Vector4 projectPointToScreen(const Vector3& point) const;
|
||||
|
||||
|
||||
private:
|
||||
typedef DescribedCreatable<Camera, Instance, sCamera, Reflection::ClassDescriptor::PERSISTENT_LOCAL> Super;
|
||||
|
||||
enum CameraInterpolation
|
||||
{
|
||||
CAM_INTERPOLATION_CONSTANT_TIME,
|
||||
CAM_INTERPOLATION_CONSTANT_SPEED,
|
||||
CAM_INTERPOLATION_NONE,
|
||||
};
|
||||
|
||||
void updateFocus();
|
||||
|
||||
bool isEditMode() const;
|
||||
bool characterZoom(float in);
|
||||
bool nonCharacterZoom(float in);
|
||||
|
||||
void tryZoomExtents(const Extents& extents);
|
||||
|
||||
float cameraToFocusDistance() const
|
||||
{
|
||||
return (cameraCoord.translation - cameraFocus.translation).magnitude();
|
||||
}
|
||||
|
||||
void setHeadingElevationDistance(float heading, float elevation, float distance);
|
||||
|
||||
// Instance
|
||||
/*override*/ bool askSetParent(const Instance* instance) const;
|
||||
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
|
||||
{
|
||||
Super::onServiceProvider(oldProvider, newProvider);
|
||||
onServiceProviderHeartbeatInstance(oldProvider, newProvider); // hooks up heartbeat
|
||||
}
|
||||
|
||||
void fixedSpeedInterpolateCamera(double elapsedTime);
|
||||
|
||||
void zoomOut(CoordinateFrame& cameraPos, CoordinateFrame& cameraFocus, float currentFocusToCameraDistance);
|
||||
|
||||
Matrix4 getProjectionPerspective() const;
|
||||
|
||||
void stopInterpolation();
|
||||
void signalInterpolationDone();
|
||||
|
||||
// HeartbeatInstance
|
||||
/*override*/ void onHeartbeat(const Heartbeat& event);
|
||||
|
||||
CameraInterpolation camInterpolation;
|
||||
|
||||
CoordinateFrame cameraCoord; // Where is the camera
|
||||
CoordinateFrame cameraFocus; // Looking at what?
|
||||
CoordinateFrame cameraCoordGoal; // Where we want to be (used for camera transitions)
|
||||
CoordinateFrame cameraFocusGoal; // Where we want to look (used for camera transitions)
|
||||
CoordinateFrame cameraCoordPrev; // Where we used to be (used for camera interpolation)
|
||||
CoordinateFrame cameraFocusPrev; // Where we used to look (used for camera interpolation)
|
||||
G3D::Vector3 cameraUpDirPrev;
|
||||
float interpolationDuration; // Time in seconds to interpolate position and focus; < 0 for constant speed interpolation
|
||||
mutable float interpolationTime; // Time in seconds that we have been doing the current interpolation
|
||||
|
||||
bool headLocked;
|
||||
|
||||
CameraType cameraType;
|
||||
shared_ptr<Instance> cameraSubject; // Guaranteed to be a CameraSubject
|
||||
float fieldOfView;
|
||||
float roll;
|
||||
|
||||
Vector2int16 viewport;
|
||||
|
||||
float panSpeed;
|
||||
float tiltSpeed;
|
||||
|
||||
CameraPanMode cameraPanMode;
|
||||
|
||||
// The image plane depth corresponding to a vertical field of
|
||||
// view, where the film size is 1x1.
|
||||
float imagePlaneDepth;
|
||||
bool hasFocalObject;
|
||||
|
||||
ICameraOwner* getCameraOwner();
|
||||
|
||||
std::vector<std::pair<CoordinateFrame, CoordinateFrame>> cameraHistoryStack;
|
||||
int currentCameraHistoryPosition;
|
||||
double lastHistoryPushTime;
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
1795
engine/app/src/DataModel/ChangeHistory.cpp
Normal file
1795
engine/app/src/DataModel/ChangeHistory.cpp
Normal file
File diff suppressed because it is too large
Load Diff
166
engine/app/src/DataModel/ChangeHistory.hpp
Normal file
166
engine/app/src/DataModel/ChangeHistory.hpp
Normal file
@@ -0,0 +1,166 @@
|
||||
#pragma once
|
||||
|
||||
#include "Tree/Service.hpp"
|
||||
#include "Utility/RunStateOwner.hpp"
|
||||
#include "Voxel/Cell.hpp"
|
||||
#include "Voxel/CellChangeListener.hpp"
|
||||
#include "Utility/SpatialRegion.hpp"
|
||||
#include <queue>
|
||||
|
||||
#include "Voxel2/GridListener.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class DataModel;
|
||||
class MegaClusterInstance;
|
||||
|
||||
extern const char* const sChangeHistoryService;
|
||||
class ChangeHistoryService
|
||||
: public DescribedCreatable<ChangeHistoryService, Instance, sChangeHistoryService, Reflection::ClassDescriptor::INTERNAL>
|
||||
, public Service
|
||||
, public Voxel::CellChangeListener
|
||||
, public Voxel2::GridListener
|
||||
{
|
||||
private:
|
||||
typedef DescribedCreatable<ChangeHistoryService, Instance, sChangeHistoryService, Reflection::ClassDescriptor::INTERNAL> Super;
|
||||
DataModel* dataModel;
|
||||
|
||||
class Item;
|
||||
class Waypoint;
|
||||
|
||||
shared_ptr<MegaClusterInstance> megaClusterInstance;
|
||||
|
||||
Waypoint* recording;
|
||||
|
||||
typedef std::list<Waypoint*> Waypoints; // must be a list and not a vector because we maintain iterators after reallocs
|
||||
Waypoints waypoints;
|
||||
|
||||
Waypoints::iterator playWaypoint; // playing nextWaypoint is a "redo"
|
||||
Waypoints::iterator unplayWaypoint; // unplaying unplayWaypoint is an "undo"
|
||||
Waypoints::iterator runStartWaypoint; // unplaying up to runStartWaypoint is a "reset"
|
||||
bool playing;
|
||||
bool enabled;
|
||||
int dataSize;
|
||||
shared_ptr<RunService> runService;
|
||||
shared_ptr<Instance> statsItem;
|
||||
|
||||
Aya::signals::scoped_connection itemAddedConnection;
|
||||
Aya::signals::scoped_connection itemRemovedConnection;
|
||||
Aya::signals::scoped_connection itemChangedConnection;
|
||||
Aya::signals::scoped_connection runTransitionConnection;
|
||||
|
||||
public:
|
||||
static const int minWaypoints = 3;
|
||||
static const int maxWaypoints = 250;
|
||||
static const int maxMemoryUsage = 250 * 1024 * 1024;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
Aggregate, // Don't allow waypoints while running. All changes are recorded and made part of the reset waypoint
|
||||
Snapshot, // When recording waypoints while running, take a snapshot of positions and velocities
|
||||
Hybrid // When recording waypoints while running, ignore unrelated position and velocity changes
|
||||
} RuntimeUndoBehavior;
|
||||
static RuntimeUndoBehavior runtimeUndoBehavior;
|
||||
|
||||
ChangeHistoryService();
|
||||
~ChangeHistoryService();
|
||||
|
||||
static void requestWaypoint(const char* name, const Instance* context);
|
||||
void resetBaseWaypoint();
|
||||
void clearWaypoints();
|
||||
|
||||
void setEnabled(bool state);
|
||||
bool isEnabled() const
|
||||
{
|
||||
return enabled;
|
||||
}
|
||||
void requestWaypoint(const char* name);
|
||||
void requestWaypoint2(std::string name)
|
||||
{
|
||||
requestWaypoint(name.c_str());
|
||||
}
|
||||
|
||||
// TODO: rename play-->redo unplay-->undo???
|
||||
bool getPlayWaypoint(std::string& name, int steps = 0) const;
|
||||
bool getUnplayWaypoint(std::string& name, int steps = 0) const;
|
||||
bool canPlay() const
|
||||
{
|
||||
std::string name;
|
||||
return getPlayWaypoint(name);
|
||||
}
|
||||
bool canUnplay() const
|
||||
{
|
||||
std::string name;
|
||||
return getUnplayWaypoint(name);
|
||||
}
|
||||
|
||||
// returns a tuple: bool state, [string name]
|
||||
shared_ptr<const Reflection::Tuple> canUnplay2();
|
||||
shared_ptr<const Reflection::Tuple> canPlay2();
|
||||
// std::string getPlayName() const { std::string name; getPlayWaypoint(name); return name; }
|
||||
// std::string getUnplayName() const { std::string name; getUnplayWaypoint(name); return name; }
|
||||
|
||||
void play();
|
||||
void playLua();
|
||||
void unplay();
|
||||
void unplayLua();
|
||||
|
||||
// to show progress dialog in studio
|
||||
boost::function<void(boost::function<void()>, std::string)> withLongRunningOperation;
|
||||
|
||||
bool isResetEnabled() const;
|
||||
void reset();
|
||||
|
||||
int getWaypointDataSize() const
|
||||
{
|
||||
return dataSize;
|
||||
}
|
||||
int getWaypointCount() const
|
||||
{
|
||||
return waypoints.size();
|
||||
}
|
||||
|
||||
Aya::signal<void()> waypointChangedSignal;
|
||||
Aya::signal<void(std::string)> undoSignal;
|
||||
Aya::signal<void(std::string)> redoSignal;
|
||||
|
||||
MegaClusterInstance* getTerrain() const
|
||||
{
|
||||
return megaClusterInstance.get();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
|
||||
|
||||
private:
|
||||
void attach();
|
||||
void dettach();
|
||||
void trimWaypoints();
|
||||
void mergeFirstTwoWaypoints();
|
||||
void computeDataSize();
|
||||
|
||||
virtual void onItemAdded(shared_ptr<Aya::Instance> item);
|
||||
virtual void onItemRemoved(shared_ptr<Aya::Instance> item);
|
||||
virtual void onItemChanged(shared_ptr<Aya::Instance> item, const Reflection::PropertyDescriptor* descriptor);
|
||||
bool isRecordable(Instance* instance);
|
||||
|
||||
/*override*/ virtual void terrainCellChanged(const Voxel::CellChangeInfo& cell);
|
||||
/*override*/ virtual void onTerrainRegionChanged(const Voxel2::Region& region);
|
||||
|
||||
void setCell(const SpatialRegion::Id& chunkPos, const Vector3int16& cellInChunk, Voxel::Cell detail, Voxel::CellMaterial material);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
Accept,
|
||||
Reject
|
||||
} CheckResult;
|
||||
CheckResult checkSettingWaypoint();
|
||||
|
||||
void onRunTransition(RunTransition event);
|
||||
void setWaypoint(const char* name);
|
||||
void setRunWaypoint();
|
||||
void reportMissedPhysicsChanges(shared_ptr<Aya::Instance> instance);
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
450
engine/app/src/DataModel/CharacterAppearance.cpp
Normal file
450
engine/app/src/DataModel/CharacterAppearance.cpp
Normal file
@@ -0,0 +1,450 @@
|
||||
|
||||
|
||||
|
||||
#include "DataModel/CharacterAppearance.hpp"
|
||||
#include "DataModel/Decal.hpp"
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
#include "DataModel/ModelInstance.hpp"
|
||||
#include "Humanoid/Humanoid.hpp"
|
||||
#include "Players.hpp"
|
||||
|
||||
DYNAMIC_FASTFLAG(UseR15Character)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
const char* const sCharacterAppearance = "CharacterAppearance";
|
||||
|
||||
const char* const sShirt = "Shirt";
|
||||
const char* const sPants = "Pants";
|
||||
const char* const sShirtGraphic = "ShirtGraphic";
|
||||
const char* const sClothing = "Clothing";
|
||||
|
||||
|
||||
Reflection::BoundProp<TextureId> ShirtGraphic::prop_Graphic("Graphic", category_Appearance, &ShirtGraphic::graphic, &ShirtGraphic::dataChanged);
|
||||
|
||||
Reflection::BoundProp<TextureId> Clothing::prop_outfit1(
|
||||
"Outfit1", category_Appearance, &Clothing::outfit1, &Clothing::dataChanged, Reflection::PropertyDescriptor::LEGACY);
|
||||
Reflection::BoundProp<TextureId> Clothing::prop_outfit2(
|
||||
"Outfit2", category_Appearance, &Clothing::outfit2, &Clothing::dataChanged, Reflection::PropertyDescriptor::LEGACY);
|
||||
|
||||
Reflection::PropDescriptor<Shirt, TextureId> Shirt::prop_ShirtTemplate(
|
||||
"ShirtTemplate", category_Appearance, &Shirt::getTemplate, &Shirt::setTemplate);
|
||||
Reflection::PropDescriptor<Pants, TextureId> Pants::prop_PantsTemplate(
|
||||
"PantsTemplate", category_Appearance, &Pants::getTemplate, &Pants::setTemplate);
|
||||
|
||||
ShirtGraphic::ShirtGraphic()
|
||||
{
|
||||
setName("Shirt Graphic");
|
||||
}
|
||||
|
||||
Clothing::Clothing()
|
||||
{
|
||||
setName("Clothing");
|
||||
}
|
||||
|
||||
Shirt::Shirt()
|
||||
{
|
||||
// Don't remove this! Gcc needs it so that Creatable works
|
||||
}
|
||||
Pants::Pants()
|
||||
{
|
||||
// Don't remove this! Gcc needs it so that Creatable works
|
||||
}
|
||||
|
||||
void ShirtGraphic::applyByMyself(Humanoid* humanoid)
|
||||
{
|
||||
if (PartInstance* torso = humanoid->getVisibleTorsoSlow())
|
||||
if (Decal* decal = torso->findFirstChildOfType<Decal>())
|
||||
decal->setTexture(graphic);
|
||||
}
|
||||
|
||||
void Clothing::applyByMyself(Humanoid* humanoid)
|
||||
{
|
||||
if (humanoid->getUseR15())
|
||||
{
|
||||
Instance* pParent = getParent();
|
||||
if (pParent)
|
||||
{
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LowerTorso")))
|
||||
answer->fireOutfitChanged();
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("UpperTorso")))
|
||||
answer->fireOutfitChanged();
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("Head")))
|
||||
answer->fireOutfitChanged();
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("RightUpperArm")))
|
||||
answer->fireOutfitChanged();
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("RightLowerArm")))
|
||||
answer->fireOutfitChanged();
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("RightHand")))
|
||||
answer->fireOutfitChanged();
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LeftUpperArm")))
|
||||
answer->fireOutfitChanged();
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LeftLowerArm")))
|
||||
answer->fireOutfitChanged();
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LeftHand")))
|
||||
answer->fireOutfitChanged();
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("RightUpperLeg")))
|
||||
answer->fireOutfitChanged();
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("RightLowerLeg")))
|
||||
answer->fireOutfitChanged();
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("RightFoot")))
|
||||
answer->fireOutfitChanged();
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LeftUpperLeg")))
|
||||
answer->fireOutfitChanged();
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LeftLowerLeg")))
|
||||
answer->fireOutfitChanged();
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LeftFoot")))
|
||||
answer->fireOutfitChanged();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PartInstance* leftLeg = humanoid->getLeftLegSlow())
|
||||
{
|
||||
leftLeg->fireOutfitChanged();
|
||||
}
|
||||
if (PartInstance* rightLeg = humanoid->getRightLegSlow())
|
||||
{
|
||||
rightLeg->fireOutfitChanged();
|
||||
}
|
||||
if (PartInstance* torso = humanoid->getVisibleTorsoSlow())
|
||||
{
|
||||
torso->fireOutfitChanged();
|
||||
}
|
||||
if (PartInstance* leftSleeve = humanoid->getLeftArmSlow())
|
||||
{
|
||||
leftSleeve->fireOutfitChanged();
|
||||
}
|
||||
if (PartInstance* rightSleeve = humanoid->getRightArmSlow())
|
||||
{
|
||||
rightSleeve->fireOutfitChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Shirt::setTemplate(TextureId value)
|
||||
{
|
||||
if (value != Clothing::outfit2)
|
||||
{
|
||||
prop_outfit2.setValue(this, value);
|
||||
raisePropertyChanged(prop_ShirtTemplate);
|
||||
}
|
||||
}
|
||||
|
||||
void Pants::setTemplate(TextureId value)
|
||||
{
|
||||
if (value != Clothing::outfit1)
|
||||
{
|
||||
prop_outfit1.setValue(this, value);
|
||||
raisePropertyChanged(prop_PantsTemplate);
|
||||
}
|
||||
}
|
||||
|
||||
const char* const sSkin = "Skin";
|
||||
|
||||
Reflection::BoundProp<BrickColor> Skin::prop_skinColor("SkinColor", category_Appearance, &Skin::skinColor, &Skin::dataChanged);
|
||||
|
||||
Skin::Skin()
|
||||
: skinColor(BrickColor::brick_226)
|
||||
{
|
||||
setName("Skin");
|
||||
}
|
||||
|
||||
void Skin::applyByMyself(Humanoid* humanoid)
|
||||
{
|
||||
if (PartInstance* head = humanoid->getHeadSlow())
|
||||
head->setColor(skinColor);
|
||||
if (PartInstance* leftLeg = humanoid->getLeftLegSlow())
|
||||
leftLeg->setColor(skinColor);
|
||||
if (PartInstance* rightLeg = humanoid->getRightLegSlow())
|
||||
rightLeg->setColor(skinColor);
|
||||
if (PartInstance* torso = humanoid->getVisibleTorsoSlow())
|
||||
torso->setColor(skinColor);
|
||||
if (PartInstance* leftSleeve = humanoid->getLeftArmSlow())
|
||||
leftSleeve->setColor(skinColor);
|
||||
if (PartInstance* rightSleeve = humanoid->getRightArmSlow())
|
||||
rightSleeve->setColor(skinColor);
|
||||
}
|
||||
|
||||
const char* const sBodyColors = "BodyColors";
|
||||
|
||||
Reflection::PropDescriptor<BodyColors, BrickColor> BodyColors::prop_HeadColor(
|
||||
"HeadColor", category_Appearance, &BodyColors::getHeadColor, &BodyColors::setHeadColor);
|
||||
Reflection::PropDescriptor<BodyColors, BrickColor> BodyColors::prop_LeftArmColor(
|
||||
"LeftArmColor", category_Appearance, &BodyColors::getLeftArmColor, &BodyColors::setLeftArmColor);
|
||||
Reflection::PropDescriptor<BodyColors, BrickColor> BodyColors::prop_RightArmColor(
|
||||
"RightArmColor", category_Appearance, &BodyColors::getRightArmColor, &BodyColors::setRightArmColor);
|
||||
Reflection::PropDescriptor<BodyColors, BrickColor> BodyColors::prop_TorsoColor(
|
||||
"TorsoColor", category_Appearance, &BodyColors::getTorsoColor, &BodyColors::setTorsoColor);
|
||||
Reflection::PropDescriptor<BodyColors, BrickColor> BodyColors::prop_LeftLegColor(
|
||||
"LeftLegColor", category_Appearance, &BodyColors::getLeftLegColor, &BodyColors::setLeftLegColor);
|
||||
Reflection::PropDescriptor<BodyColors, BrickColor> BodyColors::prop_RightLegColor(
|
||||
"RightLegColor", category_Appearance, &BodyColors::getRightLegColor, &BodyColors::setRightLegColor);
|
||||
|
||||
Reflection::PropDescriptor<BodyColors, Color3> BodyColors::prop_HeadColor3(
|
||||
"HeadColor3", category_Appearance, &BodyColors::getHeadColor3, &BodyColors::setHeadColor3);
|
||||
Reflection::PropDescriptor<BodyColors, Color3> BodyColors::prop_LeftArmColor3(
|
||||
"LeftArmColor3", category_Appearance, &BodyColors::getLeftArmColor3, &BodyColors::setLeftArmColor3);
|
||||
Reflection::PropDescriptor<BodyColors, Color3> BodyColors::prop_RightArmColor3(
|
||||
"RightArmColor3", category_Appearance, &BodyColors::getRightArmColor3, &BodyColors::setRightArmColor3);
|
||||
Reflection::PropDescriptor<BodyColors, Color3> BodyColors::prop_TorsoColor3(
|
||||
"TorsoColor3", category_Appearance, &BodyColors::getTorsoColor3, &BodyColors::setTorsoColor3);
|
||||
Reflection::PropDescriptor<BodyColors, Color3> BodyColors::prop_LeftLegColor3(
|
||||
"LeftLegColor3", category_Appearance, &BodyColors::getLeftLegColor3, &BodyColors::setLeftLegColor3);
|
||||
Reflection::PropDescriptor<BodyColors, Color3> BodyColors::prop_RightLegColor3(
|
||||
"RightLegColor3", category_Appearance, &BodyColors::getRightLegColor3, &BodyColors::setRightLegColor3);
|
||||
|
||||
BodyColors::BodyColors()
|
||||
: headColor(BrickColor::brick_226)
|
||||
, leftArmColor(BrickColor::brick_226)
|
||||
, rightArmColor(BrickColor::brick_226)
|
||||
, torsoColor(BrickColor::brick_28)
|
||||
, leftLegColor(BrickColor::brick_23)
|
||||
, rightLegColor(BrickColor::brick_23)
|
||||
, headColor3(BrickColor(BrickColor::brick_226).color3uint8())
|
||||
, leftArmColor3(BrickColor(BrickColor::brick_226).color3uint8())
|
||||
, rightArmColor3(BrickColor(BrickColor::brick_226).color3uint8())
|
||||
, torsoColor3(BrickColor(BrickColor::brick_28).color3uint8())
|
||||
, leftLegColor3(BrickColor(BrickColor::brick_23).color3uint8())
|
||||
, rightLegColor3(BrickColor(BrickColor::brick_23).color3uint8())
|
||||
{
|
||||
setName("Body Colors");
|
||||
}
|
||||
|
||||
void BodyColors::setHeadColor(BrickColor value)
|
||||
{
|
||||
if (value != headColor)
|
||||
{
|
||||
headColor = value;
|
||||
headColor3 = value.color3uint8();
|
||||
raisePropertyChanged(prop_HeadColor);
|
||||
raisePropertyChanged(prop_HeadColor3);
|
||||
}
|
||||
}
|
||||
|
||||
void BodyColors::setLeftArmColor(BrickColor value)
|
||||
{
|
||||
if (value != leftArmColor)
|
||||
{
|
||||
leftArmColor = value;
|
||||
leftArmColor3 = value.color3uint8();
|
||||
raisePropertyChanged(prop_LeftArmColor);
|
||||
raisePropertyChanged(prop_LeftArmColor3);
|
||||
}
|
||||
}
|
||||
|
||||
void BodyColors::setRightArmColor(BrickColor value)
|
||||
{
|
||||
if (value != rightArmColor)
|
||||
{
|
||||
rightArmColor = value;
|
||||
rightArmColor3 = value.color3uint8();
|
||||
raisePropertyChanged(prop_RightArmColor);
|
||||
raisePropertyChanged(prop_RightArmColor3);
|
||||
}
|
||||
}
|
||||
|
||||
void BodyColors::setTorsoColor(BrickColor value)
|
||||
{
|
||||
if (value != torsoColor)
|
||||
{
|
||||
torsoColor = value;
|
||||
torsoColor3 = value.color3uint8();
|
||||
raisePropertyChanged(prop_TorsoColor);
|
||||
raisePropertyChanged(prop_TorsoColor3);
|
||||
}
|
||||
}
|
||||
|
||||
void BodyColors::setLeftLegColor(BrickColor value)
|
||||
{
|
||||
if (value != leftLegColor)
|
||||
{
|
||||
leftLegColor = value;
|
||||
leftLegColor3 = value.color3uint8();
|
||||
raisePropertyChanged(prop_LeftLegColor);
|
||||
raisePropertyChanged(prop_LeftLegColor3);
|
||||
}
|
||||
}
|
||||
|
||||
void BodyColors::setRightLegColor(BrickColor value)
|
||||
{
|
||||
if (value != rightLegColor)
|
||||
{
|
||||
rightLegColor = value;
|
||||
rightLegColor3 = value.color3uint8();
|
||||
raisePropertyChanged(prop_RightLegColor);
|
||||
raisePropertyChanged(prop_RightLegColor3);
|
||||
}
|
||||
}
|
||||
|
||||
void BodyColors::setHeadColor3(Color3 value)
|
||||
{
|
||||
if (value != Color3(headColor3))
|
||||
{
|
||||
headColor3 = value;
|
||||
headColor = BrickColor::closest(value);
|
||||
raisePropertyChanged(prop_HeadColor3);
|
||||
raisePropertyChanged(prop_HeadColor);
|
||||
}
|
||||
}
|
||||
|
||||
void BodyColors::setLeftArmColor3(Color3 value)
|
||||
{
|
||||
if (value != Color3(leftArmColor3))
|
||||
{
|
||||
leftArmColor3 = value;
|
||||
leftArmColor = BrickColor::closest(value);
|
||||
raisePropertyChanged(prop_LeftArmColor3);
|
||||
raisePropertyChanged(prop_LeftArmColor);
|
||||
}
|
||||
}
|
||||
|
||||
void BodyColors::setRightArmColor3(Color3 value)
|
||||
{
|
||||
if (value != Color3(rightArmColor3))
|
||||
{
|
||||
rightArmColor3 = value;
|
||||
rightArmColor = BrickColor::closest(value);
|
||||
raisePropertyChanged(prop_RightArmColor3);
|
||||
raisePropertyChanged(prop_RightArmColor);
|
||||
}
|
||||
}
|
||||
|
||||
void BodyColors::setTorsoColor3(Color3 value)
|
||||
{
|
||||
if (value != Color3(torsoColor3))
|
||||
{
|
||||
torsoColor3 = value;
|
||||
torsoColor = BrickColor::closest(value);
|
||||
raisePropertyChanged(prop_TorsoColor3);
|
||||
raisePropertyChanged(prop_TorsoColor);
|
||||
}
|
||||
}
|
||||
|
||||
void BodyColors::setLeftLegColor3(Color3 value)
|
||||
{
|
||||
if (value != Color3(leftLegColor3))
|
||||
{
|
||||
leftLegColor3 = value;
|
||||
leftLegColor = BrickColor::closest(value);
|
||||
raisePropertyChanged(prop_LeftLegColor3);
|
||||
raisePropertyChanged(prop_LeftLegColor);
|
||||
}
|
||||
}
|
||||
|
||||
void BodyColors::setRightLegColor3(Color3 value)
|
||||
{
|
||||
if (value != Color3(rightLegColor3))
|
||||
{
|
||||
rightLegColor3 = value;
|
||||
rightLegColor = BrickColor::closest(value);
|
||||
raisePropertyChanged(prop_RightLegColor3);
|
||||
raisePropertyChanged(prop_RightLegColor);
|
||||
}
|
||||
}
|
||||
|
||||
void BodyColors::applyByMyself(Humanoid* humanoid)
|
||||
{
|
||||
bool hasSkin = ModelInstance::findFirstModifierOfType<Skin>(getParent()) != 0;
|
||||
if (hasSkin)
|
||||
return;
|
||||
|
||||
if (humanoid->getUseR15())
|
||||
{
|
||||
Instance* pParent = getParent();
|
||||
if (pParent)
|
||||
{
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LowerTorso")))
|
||||
answer->setColor3(torsoColor3);
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("UpperTorso")))
|
||||
answer->setColor3(torsoColor3);
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("Head")))
|
||||
answer->setColor3(headColor3);
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("RightUpperArm")))
|
||||
answer->setColor3(rightArmColor3);
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("RightLowerArm")))
|
||||
answer->setColor3(rightArmColor3);
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("RightHand")))
|
||||
answer->setColor3(rightArmColor3);
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LeftUpperArm")))
|
||||
answer->setColor3(leftArmColor3);
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LeftLowerArm")))
|
||||
answer->setColor3(leftArmColor3);
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LeftHand")))
|
||||
answer->setColor3(leftArmColor3);
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("RightUpperLeg")))
|
||||
answer->setColor3(rightLegColor3);
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("RightLowerLeg")))
|
||||
answer->setColor3(rightLegColor3);
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("RightFoot")))
|
||||
answer->setColor3(rightLegColor3);
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LeftUpperLeg")))
|
||||
answer->setColor3(leftLegColor3);
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LeftLowerLeg")))
|
||||
answer->setColor3(leftLegColor3);
|
||||
if (PartInstance* answer = Instance::fastDynamicCast<PartInstance>(pParent->findFirstChildByName("LeftFoot")))
|
||||
answer->setColor3(leftLegColor3);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PartInstance* head = humanoid->getHeadSlow())
|
||||
head->setColor3(headColor3);
|
||||
if (PartInstance* leftLeg = humanoid->getLeftLegSlow())
|
||||
leftLeg->setColor3(leftLegColor3);
|
||||
if (PartInstance* rightLeg = humanoid->getRightLegSlow())
|
||||
rightLeg->setColor3(rightLegColor3);
|
||||
if (PartInstance* torso = humanoid->getVisibleTorsoSlow())
|
||||
torso->setColor3(torsoColor3);
|
||||
if (PartInstance* leftSleeve = humanoid->getLeftArmSlow())
|
||||
leftSleeve->setColor3(leftArmColor3);
|
||||
if (PartInstance* rightSleeve = humanoid->getRightArmSlow())
|
||||
rightSleeve->setColor3(rightArmColor3);
|
||||
}
|
||||
}
|
||||
|
||||
void LegacyCharacterAppearance::apply()
|
||||
{
|
||||
if (Network::Players::backendProcessing(this, false))
|
||||
Super::apply();
|
||||
}
|
||||
void CharacterAppearance::apply()
|
||||
{
|
||||
if (Humanoid* humanoid = Humanoid::modelIsCharacter(getParent()))
|
||||
applyByMyself(humanoid);
|
||||
}
|
||||
|
||||
void CharacterAppearance::onAncestorChanged(const AncestorChanged& event)
|
||||
{
|
||||
Super::onAncestorChanged(event);
|
||||
|
||||
// Search for a Humanoid sibling:
|
||||
if (event.child == this)
|
||||
{
|
||||
|
||||
if (event.newParent)
|
||||
{
|
||||
Aya::Humanoid* humanoid = Humanoid::modelIsCharacter(event.newParent);
|
||||
if (humanoid)
|
||||
{
|
||||
applyByMyself(humanoid);
|
||||
}
|
||||
}
|
||||
if (event.oldParent)
|
||||
{
|
||||
Aya::Humanoid* humanoid = Humanoid::modelIsCharacter(event.oldParent);
|
||||
if (humanoid)
|
||||
{
|
||||
applyByMyself(humanoid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CharacterAppearance::askSetParent(const Instance* instance) const
|
||||
{
|
||||
return Instance::fastDynamicCast<ModelInstance>(instance) != NULL;
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
242
engine/app/src/DataModel/CharacterAppearance.hpp
Normal file
242
engine/app/src/DataModel/CharacterAppearance.hpp
Normal file
@@ -0,0 +1,242 @@
|
||||
#pragma once
|
||||
|
||||
#include "DataModel/GlobalSettings.hpp"
|
||||
#include "Utility/BrickColor.hpp"
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
#include "DataModel/IModelModifier.hpp"
|
||||
#include "Utility/TextureId.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class Humanoid;
|
||||
|
||||
extern const char* const sCharacterAppearance;
|
||||
class AyaBaseClass CharacterAppearance
|
||||
: public DescribedNonCreatable<CharacterAppearance, Instance, sCharacterAppearance>
|
||||
, public IModelModifier
|
||||
{
|
||||
private:
|
||||
typedef Instance Super;
|
||||
|
||||
public:
|
||||
virtual void apply();
|
||||
|
||||
protected:
|
||||
virtual void onAncestorChanged(const AncestorChanged& event);
|
||||
virtual bool askSetParent(const Instance* instance) const;
|
||||
|
||||
private:
|
||||
virtual void applyByMyself(Humanoid* humanoid) = 0;
|
||||
};
|
||||
|
||||
class LegacyCharacterAppearance : public CharacterAppearance
|
||||
{
|
||||
private:
|
||||
typedef CharacterAppearance Super;
|
||||
|
||||
public:
|
||||
// Hack: This apply() function is ugly, but necessary because
|
||||
// this class changes properties of other objects (like
|
||||
// Part colors and Decal images). However, to avoid
|
||||
// crosstalk the apply function should only do something
|
||||
// in the backend case.
|
||||
/*override*/ void apply();
|
||||
};
|
||||
|
||||
// Old-style T-shirts
|
||||
extern const char* const sShirtGraphic;
|
||||
class ShirtGraphic : public DescribedCreatable<ShirtGraphic, LegacyCharacterAppearance, sShirtGraphic>
|
||||
{
|
||||
public:
|
||||
TextureId graphic;
|
||||
static Reflection::BoundProp<TextureId> prop_Graphic;
|
||||
ShirtGraphic();
|
||||
|
||||
protected:
|
||||
/*override*/ void applyByMyself(Humanoid* humanoid);
|
||||
|
||||
private:
|
||||
void dataChanged(const Reflection::PropertyDescriptor&)
|
||||
{
|
||||
CharacterAppearance::apply();
|
||||
}
|
||||
};
|
||||
|
||||
extern const char* const sClothing;
|
||||
class Clothing : public DescribedNonCreatable<Clothing, CharacterAppearance, sClothing>
|
||||
{
|
||||
friend class CharacterAppearance;
|
||||
|
||||
public:
|
||||
TextureId outfit1;
|
||||
TextureId outfit2;
|
||||
|
||||
static Reflection::BoundProp<TextureId> prop_outfit1;
|
||||
static Reflection::BoundProp<TextureId> prop_outfit2;
|
||||
|
||||
Clothing();
|
||||
|
||||
virtual TextureId getTemplate() const
|
||||
{
|
||||
AYAASSERT(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
protected:
|
||||
/*override*/ void applyByMyself(Humanoid* humanoid);
|
||||
void dataChanged(const Reflection::PropertyDescriptor&)
|
||||
{
|
||||
CharacterAppearance::apply();
|
||||
}
|
||||
};
|
||||
|
||||
extern const char* const sPants;
|
||||
class Pants : public DescribedCreatable<Pants, Clothing, sPants>
|
||||
{
|
||||
public:
|
||||
Pants();
|
||||
static Reflection::PropDescriptor<Pants, TextureId> prop_PantsTemplate;
|
||||
TextureId getTemplate() const
|
||||
{
|
||||
return outfit1;
|
||||
}
|
||||
void setTemplate(TextureId value);
|
||||
};
|
||||
|
||||
extern const char* const sShirt;
|
||||
class Shirt : public DescribedCreatable<Shirt, Clothing, sShirt>
|
||||
{
|
||||
public:
|
||||
Shirt();
|
||||
static Reflection::PropDescriptor<Shirt, TextureId> prop_ShirtTemplate;
|
||||
TextureId getTemplate() const
|
||||
{
|
||||
return outfit2;
|
||||
}
|
||||
void setTemplate(TextureId value);
|
||||
};
|
||||
|
||||
extern const char* const sBodyColors;
|
||||
class BodyColors : public DescribedCreatable<BodyColors, LegacyCharacterAppearance, sBodyColors>
|
||||
{
|
||||
BrickColor headColor;
|
||||
Color3uint8 headColor3;
|
||||
BrickColor leftArmColor;
|
||||
Color3uint8 leftArmColor3;
|
||||
BrickColor rightArmColor;
|
||||
Color3uint8 rightArmColor3;
|
||||
BrickColor torsoColor;
|
||||
Color3uint8 torsoColor3;
|
||||
BrickColor leftLegColor;
|
||||
Color3uint8 leftLegColor3;
|
||||
BrickColor rightLegColor;
|
||||
Color3uint8 rightLegColor3;
|
||||
|
||||
BrickColor getHeadColor() const
|
||||
{
|
||||
return headColor;
|
||||
}
|
||||
BrickColor getLeftArmColor() const
|
||||
{
|
||||
return leftArmColor;
|
||||
}
|
||||
BrickColor getRightArmColor() const
|
||||
{
|
||||
return rightArmColor;
|
||||
}
|
||||
BrickColor getTorsoColor() const
|
||||
{
|
||||
return torsoColor;
|
||||
}
|
||||
BrickColor getLeftLegColor() const
|
||||
{
|
||||
return leftLegColor;
|
||||
}
|
||||
BrickColor getRightLegColor() const
|
||||
{
|
||||
return rightLegColor;
|
||||
}
|
||||
|
||||
Color3 getHeadColor3() const
|
||||
{
|
||||
return headColor3;
|
||||
}
|
||||
Color3 getLeftArmColor3() const
|
||||
{
|
||||
return leftArmColor3;
|
||||
}
|
||||
Color3 getRightArmColor3() const
|
||||
{
|
||||
return rightArmColor3;
|
||||
}
|
||||
Color3 getTorsoColor3() const
|
||||
{
|
||||
return torsoColor3;
|
||||
}
|
||||
Color3 getLeftLegColor3() const
|
||||
{
|
||||
return leftLegColor3;
|
||||
}
|
||||
Color3 getRightLegColor3() const
|
||||
{
|
||||
return rightLegColor3;
|
||||
}
|
||||
|
||||
void setHeadColor(BrickColor value);
|
||||
void setLeftArmColor(BrickColor value);
|
||||
void setRightArmColor(BrickColor value);
|
||||
void setTorsoColor(BrickColor value);
|
||||
void setLeftLegColor(BrickColor value);
|
||||
void setRightLegColor(BrickColor value);
|
||||
|
||||
void setHeadColor3(Color3 value);
|
||||
void setLeftArmColor3(Color3 value);
|
||||
void setRightArmColor3(Color3 value);
|
||||
void setTorsoColor3(Color3 value);
|
||||
void setLeftLegColor3(Color3 value);
|
||||
void setRightLegColor3(Color3 value);
|
||||
|
||||
public:
|
||||
static Reflection::PropDescriptor<BodyColors, BrickColor> prop_HeadColor;
|
||||
static Reflection::PropDescriptor<BodyColors, BrickColor> prop_LeftArmColor;
|
||||
static Reflection::PropDescriptor<BodyColors, BrickColor> prop_RightArmColor;
|
||||
static Reflection::PropDescriptor<BodyColors, BrickColor> prop_TorsoColor;
|
||||
static Reflection::PropDescriptor<BodyColors, BrickColor> prop_LeftLegColor;
|
||||
static Reflection::PropDescriptor<BodyColors, BrickColor> prop_RightLegColor;
|
||||
|
||||
static Reflection::PropDescriptor<BodyColors, Color3> prop_HeadColor3;
|
||||
static Reflection::PropDescriptor<BodyColors, Color3> prop_LeftArmColor3;
|
||||
static Reflection::PropDescriptor<BodyColors, Color3> prop_RightArmColor3;
|
||||
static Reflection::PropDescriptor<BodyColors, Color3> prop_TorsoColor3;
|
||||
static Reflection::PropDescriptor<BodyColors, Color3> prop_LeftLegColor3;
|
||||
static Reflection::PropDescriptor<BodyColors, Color3> prop_RightLegColor3;
|
||||
|
||||
BodyColors();
|
||||
|
||||
private:
|
||||
virtual void applyByMyself(Humanoid* humanoid);
|
||||
void dataChanged(const Reflection::PropertyDescriptor&)
|
||||
{
|
||||
CharacterAppearance::apply();
|
||||
}
|
||||
};
|
||||
|
||||
extern const char* const sSkin;
|
||||
class Skin : public DescribedCreatable<Skin, LegacyCharacterAppearance, sSkin>
|
||||
{
|
||||
BrickColor skinColor;
|
||||
|
||||
public:
|
||||
static Reflection::BoundProp<BrickColor> prop_skinColor;
|
||||
Skin();
|
||||
|
||||
private:
|
||||
virtual void applyByMyself(Humanoid* humanoid);
|
||||
void dataChanged(const Reflection::PropertyDescriptor&)
|
||||
{
|
||||
CharacterAppearance::apply();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
131
engine/app/src/DataModel/CharacterMesh.cpp
Normal file
131
engine/app/src/DataModel/CharacterMesh.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
|
||||
|
||||
#include "DataModel/CharacterMesh.hpp"
|
||||
#include "Humanoid/Humanoid.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
const char* const sCharacterMesh = "CharacterMesh";
|
||||
|
||||
|
||||
static const Reflection::EnumPropDescriptor<CharacterMesh, CharacterMesh::BodyPart> prop_bodyPart(
|
||||
"BodyPart", category_Data, &CharacterMesh::getBodyPart, &CharacterMesh::setBodyPart);
|
||||
Reflection::BoundProp<int64_t> CharacterMesh::prop_baseTextureAssetId("BaseTextureId", "Data", &CharacterMesh::baseTextureAssetId);
|
||||
Reflection::BoundProp<int64_t> CharacterMesh::prop_overlayTextureAssetId("OverlayTextureId", "Data", &CharacterMesh::overlayTextureAssetId);
|
||||
Reflection::BoundProp<int64_t> CharacterMesh::prop_meshAssetId("MeshId", "Data", &CharacterMesh::meshAssetId);
|
||||
REFLECTION_END();
|
||||
|
||||
namespace Reflection
|
||||
{
|
||||
template<>
|
||||
EnumDesc<CharacterMesh::BodyPart>::EnumDesc()
|
||||
: EnumDescriptor("BodyPart")
|
||||
|
||||
{
|
||||
addPair(CharacterMesh::HEAD, "Head");
|
||||
addPair(CharacterMesh::TORSO, "Torso");
|
||||
addPair(CharacterMesh::LEFTARM, "LeftArm");
|
||||
addPair(CharacterMesh::RIGHTARM, "RightArm");
|
||||
addPair(CharacterMesh::LEFTLEG, "LeftLeg");
|
||||
addPair(CharacterMesh::RIGHTLEG, "RightLeg");
|
||||
}
|
||||
template<>
|
||||
CharacterMesh::BodyPart& Variant::convert<CharacterMesh::BodyPart>(void)
|
||||
{
|
||||
return genericConvert<CharacterMesh::BodyPart>();
|
||||
}
|
||||
} // namespace Reflection
|
||||
|
||||
template<>
|
||||
bool StringConverter<CharacterMesh::BodyPart>::convertToValue(const std::string& text, CharacterMesh::BodyPart& value)
|
||||
{
|
||||
return Reflection::EnumDesc<CharacterMesh::BodyPart>::singleton().convertToValue(text.c_str(), value);
|
||||
}
|
||||
|
||||
CharacterMesh::CharacterMesh()
|
||||
: DescribedCreatable<CharacterMesh, CharacterAppearance, sCharacterMesh>()
|
||||
, bodyPart(HEAD)
|
||||
, baseTextureAssetId(0)
|
||||
, overlayTextureAssetId(0)
|
||||
, meshAssetId(0)
|
||||
{
|
||||
setName(sCharacterMesh);
|
||||
}
|
||||
|
||||
void CharacterMesh::onPropertyChanged(const Reflection::PropertyDescriptor& descriptor)
|
||||
{
|
||||
CharacterAppearance::apply();
|
||||
}
|
||||
|
||||
void CharacterMesh::setBodyPart(BodyPart value)
|
||||
{
|
||||
if (bodyPart != value)
|
||||
{
|
||||
bodyPart = value;
|
||||
|
||||
raisePropertyChanged(prop_bodyPart);
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterMesh::applyByMyself(Humanoid* humanoid)
|
||||
{
|
||||
// re-using fireOutfitChanged. consider: own event.
|
||||
// right now, both end up doing the exact same thing, so reuse for now.
|
||||
if (PartInstance* leftLeg = humanoid->getLeftLegSlow())
|
||||
{
|
||||
leftLeg->fireOutfitChanged();
|
||||
}
|
||||
if (PartInstance* rightLeg = humanoid->getRightLegSlow())
|
||||
{
|
||||
rightLeg->fireOutfitChanged();
|
||||
}
|
||||
if (PartInstance* torso = humanoid->getTorsoSlow())
|
||||
{
|
||||
torso->fireOutfitChanged();
|
||||
}
|
||||
if (PartInstance* leftSleeve = humanoid->getLeftArmSlow())
|
||||
{
|
||||
leftSleeve->fireOutfitChanged();
|
||||
}
|
||||
if (PartInstance* rightSleeve = humanoid->getRightArmSlow())
|
||||
{
|
||||
rightSleeve->fireOutfitChanged();
|
||||
}
|
||||
}
|
||||
|
||||
TextureId CharacterMesh::getBaseTextureId() const
|
||||
{
|
||||
if (baseTextureAssetId != 0)
|
||||
{
|
||||
return TextureId(Aya::format("ayaassetid://%d", baseTextureAssetId));
|
||||
}
|
||||
else
|
||||
{
|
||||
return TextureId::nullTexture();
|
||||
}
|
||||
}
|
||||
TextureId CharacterMesh::getOverlayTextureId() const
|
||||
{
|
||||
if (overlayTextureAssetId != 0)
|
||||
{
|
||||
return TextureId(Aya::format("ayaassetid://%d", overlayTextureAssetId));
|
||||
}
|
||||
else
|
||||
{
|
||||
return TextureId::nullTexture();
|
||||
}
|
||||
}
|
||||
MeshId CharacterMesh::getMeshId() const
|
||||
{
|
||||
if (meshAssetId != 0)
|
||||
{
|
||||
return MeshId(Aya::format("ayaassetid://%d", meshAssetId));
|
||||
}
|
||||
else
|
||||
{
|
||||
return MeshId();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
57
engine/app/src/DataModel/CharacterMesh.hpp
Normal file
57
engine/app/src/DataModel/CharacterMesh.hpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utility/TextureId.hpp"
|
||||
#include "Utility/MeshId.hpp"
|
||||
#include "CharacterAppearance.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class Humanoid;
|
||||
|
||||
extern const char* const sCharacterMesh;
|
||||
class CharacterMesh : public DescribedCreatable<CharacterMesh, CharacterAppearance, sCharacterMesh>
|
||||
{
|
||||
public:
|
||||
enum BodyPart
|
||||
{
|
||||
HEAD = 0,
|
||||
TORSO = 1,
|
||||
LEFTARM = 2,
|
||||
RIGHTARM = 3,
|
||||
LEFTLEG = 4,
|
||||
RIGHTLEG = 5
|
||||
};
|
||||
|
||||
CharacterMesh();
|
||||
|
||||
|
||||
BodyPart getBodyPart() const
|
||||
{
|
||||
return bodyPart;
|
||||
}
|
||||
void setBodyPart(BodyPart value);
|
||||
|
||||
|
||||
int64_t baseTextureAssetId;
|
||||
int64_t overlayTextureAssetId;
|
||||
int64_t meshAssetId;
|
||||
|
||||
static Reflection::BoundProp<int64_t> prop_baseTextureAssetId;
|
||||
static Reflection::BoundProp<int64_t> prop_overlayTextureAssetId;
|
||||
static Reflection::BoundProp<int64_t> prop_meshAssetId;
|
||||
|
||||
TextureId getBaseTextureId() const;
|
||||
TextureId getOverlayTextureId() const;
|
||||
MeshId getMeshId() const;
|
||||
|
||||
protected:
|
||||
virtual void applyByMyself(Humanoid* humanoid);
|
||||
/*override*/ void onPropertyChanged(const Reflection::PropertyDescriptor& descriptor);
|
||||
|
||||
private:
|
||||
BodyPart bodyPart;
|
||||
};
|
||||
|
||||
|
||||
} // namespace Aya
|
||||
166
engine/app/src/DataModel/ChatService.cpp
Normal file
166
engine/app/src/DataModel/ChatService.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
|
||||
|
||||
#include "DataModel/ChatService.hpp"
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
#include "DataModel/Workspace.hpp"
|
||||
#include "DataModel/HttpRbxApiService.hpp"
|
||||
#include "Xml/WebParser.hpp"
|
||||
|
||||
#include "WebChatFilter.hpp"
|
||||
#include "Players.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
const char* const sChatService = "Chat";
|
||||
|
||||
|
||||
static Reflection::BoundFuncDesc<ChatService, void(shared_ptr<Instance>, std::string, ChatService::ChatColor)> func_chat(
|
||||
&ChatService::chat, "Chat", "partOrCharacter", "message", "color", ChatService::CHAT_BLUE, Security::None);
|
||||
|
||||
static Reflection::BoundYieldFuncDesc<ChatService, std::string(std::string, shared_ptr<Instance>)> func_filterString(
|
||||
&ChatService::filterStringForPlayer, "FilterStringForPlayerAsync", "stringToFilter", "playerToFilterFor", Security::None);
|
||||
|
||||
static Reflection::RemoteEventDesc<ChatService, void(shared_ptr<Instance>, std::string, ChatService::ChatColor)> event_Chatted(
|
||||
&ChatService::chattedSignal, "Chatted", "part", "message", "color", Security::None, Reflection::RemoteEventCommon::SCRIPTING,
|
||||
Reflection::RemoteEventCommon::BROADCAST);
|
||||
REFLECTION_END();
|
||||
|
||||
namespace Reflection
|
||||
{
|
||||
template<>
|
||||
EnumDesc<ChatService::ChatColor>::EnumDesc()
|
||||
: EnumDescriptor("ChatColor")
|
||||
{
|
||||
addPair(ChatService::CHAT_BLUE, "Blue");
|
||||
addPair(ChatService::CHAT_GREEN, "Green");
|
||||
addPair(ChatService::CHAT_RED, "Red");
|
||||
}
|
||||
|
||||
template<>
|
||||
ChatService::ChatColor& Variant::convert<ChatService::ChatColor>(void)
|
||||
{
|
||||
return genericConvert<ChatService::ChatColor>();
|
||||
}
|
||||
} // namespace Reflection
|
||||
|
||||
template<>
|
||||
bool Aya::StringConverter<ChatService::ChatColor>::convertToValue(const std::string& text, ChatService::ChatColor& value)
|
||||
{
|
||||
return Reflection::EnumDesc<ChatService::ChatColor>::singleton().convertToValue(text.c_str(), value);
|
||||
}
|
||||
ChatService::ChatService()
|
||||
{
|
||||
setName(sChatService);
|
||||
}
|
||||
|
||||
void ChatService::chat(shared_ptr<Instance> instance, std::string message, ChatService::ChatColor chatColor)
|
||||
{
|
||||
if (!instance)
|
||||
throw std::runtime_error("partOrCharacter must be non-nil");
|
||||
if (!Workspace::contextInWorkspace(instance.get()))
|
||||
throw std::runtime_error("partOrCharacter must be in the Workspace");
|
||||
|
||||
if (instance->fastDynamicCast<ModelInstance>())
|
||||
{
|
||||
Network::Players* players = ServiceProvider::create<Network::Players>(this);
|
||||
if (!players->playerFromCharacter(instance))
|
||||
throw std::runtime_error("partOrCharacter is not a legal character");
|
||||
}
|
||||
else if (!instance->fastDynamicCast<PartInstance>())
|
||||
{
|
||||
throw std::runtime_error("partOrCharacter must be a Part or a Character");
|
||||
}
|
||||
|
||||
if (message.empty())
|
||||
throw std::runtime_error("message must be non-empty");
|
||||
|
||||
event_Chatted.fireAndReplicateEvent(this, instance, message, chatColor);
|
||||
}
|
||||
|
||||
void ChatService::gotFilteredStringSuccess(std::string response, Network::Player* player, boost::function<void(std::string)> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
if (!response.empty())
|
||||
{
|
||||
Reflection::Variant jsonVariant;
|
||||
WebParser::parseJSONObject(response, jsonVariant);
|
||||
if (jsonVariant.isType<shared_ptr<const Reflection::ValueTable>>())
|
||||
{
|
||||
shared_ptr<const Reflection::ValueTable> filteredStringTable = jsonVariant.cast<shared_ptr<const Reflection::ValueTable>>();
|
||||
Reflection::Variant dataVariant = filteredStringTable->at("data");
|
||||
|
||||
if (dataVariant.isType<shared_ptr<const Reflection::ValueTable>>())
|
||||
{
|
||||
shared_ptr<const Reflection::ValueTable> dataTable = dataVariant.cast<shared_ptr<const Reflection::ValueTable>>();
|
||||
Reflection::Variant whiteListPolicyFilterResult = dataTable->at(kWebChatWhiteListPolicy);
|
||||
Reflection::Variant blackListPolicyFilterResult = dataTable->at(kWebChatBlackListPolicy);
|
||||
|
||||
if (player->getChatFilterType() == Network::Player::CHAT_FILTER_WHITELIST)
|
||||
{
|
||||
std::string whiteListString = whiteListPolicyFilterResult.get<std::string>();
|
||||
resumeFunction(whiteListString);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string blackListString = blackListPolicyFilterResult.get<std::string>();
|
||||
resumeFunction(blackListString);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
errorFunction("ChatService:FilterString could not parse response");
|
||||
}
|
||||
else
|
||||
{
|
||||
errorFunction("ChatService:FilterString returned an empty response");
|
||||
}
|
||||
}
|
||||
|
||||
void ChatService::gotFilterStringError(std::string error, boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
errorFunction(error);
|
||||
}
|
||||
|
||||
|
||||
void ChatService::filterStringForPlayer(std::string stringToFilter, shared_ptr<Instance> playerToFilterFor,
|
||||
boost::function<void(std::string)> resumeFunction, boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
if (!Network::Players::serverIsPresent(this))
|
||||
{
|
||||
errorFunction("ChatService:FilterString called on a client, only works from server.");
|
||||
return;
|
||||
}
|
||||
|
||||
Network::Player* playerFilter = Instance::fastDynamicCast<Network::Player>(playerToFilterFor.get());
|
||||
|
||||
if (!playerFilter)
|
||||
{
|
||||
errorFunction("ChatService:FilterString called without a valid Player object.");
|
||||
return;
|
||||
}
|
||||
|
||||
std::stringstream params;
|
||||
Aya::HttpAux::AdditionalHeaders headers;
|
||||
|
||||
{
|
||||
params << "filters=" << kWebChatWhiteListPolicy;
|
||||
params << "&filters=" << kWebChatBlackListPolicy;
|
||||
params << "&text=" << Http::urlEncode(stringToFilter);
|
||||
}
|
||||
|
||||
if (Aya::HttpRbxApiService* apiService = Aya::ServiceProvider::find<Aya::HttpRbxApiService>(this))
|
||||
{
|
||||
apiService->postAsync("moderation/filtertext", params.str(), true, Aya::PRIORITY_DEFAULT, Aya::HttpService::APPLICATION_URLENCODED,
|
||||
boost::bind(&ChatService::gotFilteredStringSuccess, this, _1, playerFilter, resumeFunction, errorFunction),
|
||||
boost::bind(&ChatService::gotFilterStringError, this, _1, errorFunction));
|
||||
}
|
||||
else
|
||||
{
|
||||
errorFunction("ChatService:FilterString could not find ApiService.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
39
engine/app/src/DataModel/ChatService.hpp
Normal file
39
engine/app/src/DataModel/ChatService.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "Tree/Service.hpp"
|
||||
#include "Tree/Instance.hpp"
|
||||
#include "Player.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
class PartInstance;
|
||||
|
||||
extern const char* const sChatService;
|
||||
|
||||
class ChatService
|
||||
: public DescribedCreatable<ChatService, Instance, sChatService, Reflection::ClassDescriptor::INTERNAL>
|
||||
, public Service
|
||||
{
|
||||
private:
|
||||
void gotFilteredStringSuccess(std::string response, Network::Player* player, boost::function<void(std::string)> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction);
|
||||
void gotFilterStringError(std::string error, boost::function<void(std::string)> errorFunction);
|
||||
|
||||
public:
|
||||
enum ChatColor
|
||||
{
|
||||
CHAT_BLUE,
|
||||
CHAT_GREEN,
|
||||
CHAT_RED
|
||||
};
|
||||
|
||||
ChatService();
|
||||
|
||||
void chat(shared_ptr<Instance> instance, std::string message, ChatService::ChatColor chatColor);
|
||||
|
||||
void filterStringForPlayer(std::string stringToFilter, shared_ptr<Instance> playerToFilterFor, boost::function<void(std::string)> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction);
|
||||
|
||||
Aya::remote_signal<void(shared_ptr<Instance>, std::string, ChatService::ChatColor)> chattedSignal;
|
||||
};
|
||||
} // namespace Aya
|
||||
1152
engine/app/src/DataModel/ChromiumFrame.cpp
Normal file
1152
engine/app/src/DataModel/ChromiumFrame.cpp
Normal file
File diff suppressed because it is too large
Load Diff
337
engine/app/src/DataModel/ChromiumFrame.hpp
Normal file
337
engine/app/src/DataModel/ChromiumFrame.hpp
Normal file
@@ -0,0 +1,337 @@
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
|
||||
#include "DataModel/GuiObject.hpp"
|
||||
#include "DataModel/GuiMixin.hpp"
|
||||
#include "Utility/Sound.hpp"
|
||||
#include "Utility/SoundChannel.hpp"
|
||||
#include "fmod_common.h"
|
||||
|
||||
#ifdef ENABLE_CHROMIUM_FRAMES
|
||||
#include <include/cef_app.h>
|
||||
#include <include/cef_base.h>
|
||||
#endif
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class PartInstance;
|
||||
|
||||
enum ChromiumConsoleMessageSeverity
|
||||
{
|
||||
Default,
|
||||
Verbose,
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
Fatal
|
||||
};
|
||||
|
||||
enum ChromiumLoadState
|
||||
{
|
||||
Stopped,
|
||||
Loaded,
|
||||
Loading,
|
||||
LoadError
|
||||
};
|
||||
|
||||
enum ChromiumMouseCursor
|
||||
{
|
||||
Pointer,
|
||||
Cross,
|
||||
Hand,
|
||||
IBeam,
|
||||
Wait,
|
||||
Help,
|
||||
EastResize,
|
||||
NorthResize,
|
||||
NortheastResize,
|
||||
NorthwestResize,
|
||||
SouthResize,
|
||||
SoutheastResize,
|
||||
SouthwestResize,
|
||||
WestResize,
|
||||
NorthSouthResize,
|
||||
EastWestResize,
|
||||
NortheastSouthwestResize,
|
||||
NorthwestSoutheastResize,
|
||||
ColumnResize,
|
||||
RowResize,
|
||||
MiddlePanning,
|
||||
EastPanning,
|
||||
NorthPanning,
|
||||
NortheastPanning,
|
||||
NorthwestPanning,
|
||||
SouthPanning,
|
||||
SoutheastPanning,
|
||||
SouthwestPanning,
|
||||
WestPanning,
|
||||
Move,
|
||||
VerticalText,
|
||||
Cell,
|
||||
ContextMenu,
|
||||
Alias,
|
||||
Progress,
|
||||
NoDrop,
|
||||
Copy,
|
||||
None,
|
||||
NotAllowed,
|
||||
ZoomIn,
|
||||
ZoomOut,
|
||||
Grab,
|
||||
Grabbing,
|
||||
MiddlePanningVertical,
|
||||
MiddlePanningHorizontal,
|
||||
Custom,
|
||||
DndNone,
|
||||
DndMove,
|
||||
DndCopy,
|
||||
DndLink
|
||||
};
|
||||
|
||||
// fwd decl
|
||||
class AyaCefRenderer;
|
||||
class AyaCefClient;
|
||||
|
||||
extern const char* const sChromiumFrame;
|
||||
|
||||
class ChromiumFrame
|
||||
: public DescribedCreatable<ChromiumFrame, GuiLabel, sChromiumFrame>
|
||||
, public GuiImageMixin
|
||||
{
|
||||
typedef DescribedCreatable<ChromiumFrame, GuiLabel, sChromiumFrame> Super;
|
||||
|
||||
public:
|
||||
ChromiumFrame();
|
||||
~ChromiumFrame();
|
||||
|
||||
void initialize();
|
||||
void uninitialize();
|
||||
|
||||
void mouseLeftClick(int x, int y, bool isDown = true);
|
||||
void mouseMiddleClick(int x, int y, bool isDown = true);
|
||||
void mouseRightClick(int x, int y, bool isDown = true);
|
||||
void mouseWheel(int x, int y, int deltaX, int deltaY);
|
||||
void mouseMove(int x, int y);
|
||||
void keyEvent(int keyCode, int modifiers, bool isKeyDown = true);
|
||||
|
||||
std::string runJS(std::string code);
|
||||
|
||||
void refresh();
|
||||
void refreshNoCache();
|
||||
void backward();
|
||||
void forward();
|
||||
void cancel();
|
||||
void focus();
|
||||
void unfocus();
|
||||
Color3 getColorAtPixel(int x, int y);
|
||||
|
||||
bool getIsLoaded() const
|
||||
{
|
||||
return (loadState == ChromiumLoadState::Loaded);
|
||||
}
|
||||
bool getCanPageBackward() const
|
||||
{
|
||||
return canPageBackward;
|
||||
}
|
||||
void setCanPageBackward(bool value);
|
||||
bool getCanPageForward() const
|
||||
{
|
||||
return canPageForward;
|
||||
}
|
||||
void setCanPageForward(bool value);
|
||||
|
||||
bool getInitialized() const
|
||||
{
|
||||
return initialized;
|
||||
}
|
||||
void setInitializedInternal(bool value);
|
||||
|
||||
std::string getPageTitle() const
|
||||
{
|
||||
return pageTitle;
|
||||
}
|
||||
void setPageTitleInternal(std::string value);
|
||||
|
||||
int getStatusCode() const
|
||||
{
|
||||
return statusCode;
|
||||
}
|
||||
void setStatusCodeInternal(int value);
|
||||
|
||||
ChromiumLoadState getLoadState() const
|
||||
{
|
||||
return loadState;
|
||||
}
|
||||
void setLoadStateInternal(ChromiumLoadState value);
|
||||
|
||||
Vector2 getResolution() const
|
||||
{
|
||||
return this->resolution;
|
||||
}
|
||||
void setResolution(Vector2 resolution);
|
||||
|
||||
Vector2 getMousePosition() const
|
||||
{
|
||||
return this->mousePosition;
|
||||
}
|
||||
void setMousePosition(Vector2 position);
|
||||
|
||||
std::string getURL() const
|
||||
{
|
||||
return this->url;
|
||||
}
|
||||
void setURL(std::string url);
|
||||
void setURLInternal(std::string url); // called by AyaCefClient::OnLoadStart
|
||||
|
||||
ChromiumMouseCursor getMouseCursor() const
|
||||
{
|
||||
return this->mouseCursor;
|
||||
}
|
||||
void setMouseCursorInternal(ChromiumMouseCursor value);
|
||||
|
||||
Vector2 getScrollOffset() const
|
||||
{
|
||||
return this->scrollOffset;
|
||||
}
|
||||
void setScrollOffsetInternal(Vector2 value);
|
||||
|
||||
float getZoom() const
|
||||
{
|
||||
return this->zoom;
|
||||
}
|
||||
void setZoom(float value);
|
||||
void setZoomInternal(float value);
|
||||
|
||||
float getVolume() const
|
||||
{
|
||||
return this->volume;
|
||||
}
|
||||
void setVolume(float value);
|
||||
|
||||
float getPitch() const
|
||||
{
|
||||
return this->pitch;
|
||||
}
|
||||
void setPitch(float value);
|
||||
|
||||
bool isPlaying() const
|
||||
{
|
||||
return this->playing;
|
||||
}
|
||||
void setIsPlayingInternal(bool value);
|
||||
|
||||
void setWorldAudioEnabled(bool value);
|
||||
bool getWorldAudioEnabled() const
|
||||
{
|
||||
return this->worldAudioEnabled;
|
||||
}
|
||||
|
||||
void updateWorldAudio();
|
||||
|
||||
float getWorldAudioMinDistance() const
|
||||
{
|
||||
return this->worldAudioMinDistance;
|
||||
}
|
||||
void setWorldAudioMinDistance(float value);
|
||||
|
||||
float getWorldAudioMaxDistance() const
|
||||
{
|
||||
return this->worldAudioMaxDistance;
|
||||
}
|
||||
void setWorldAudioMaxDistance(float value);
|
||||
|
||||
Soundscape::RollOffMode getWorldAudioRollOff() const
|
||||
{
|
||||
return this->worldAudioRollOff;
|
||||
}
|
||||
void setWorldAudioRollOff(Soundscape::RollOffMode value);
|
||||
|
||||
typedef std::pair<int, float*> ChromiumFrameAudioData;
|
||||
|
||||
std::deque<ChromiumFrameAudioData> audioData;
|
||||
std::mutex audioMutex;
|
||||
|
||||
void startSound(int channels, int sampleRate, int framesPerBuffer);
|
||||
void pushSoundData(const float** data, int frames, int64_t pts);
|
||||
ChromiumFrameAudioData popSoundData();
|
||||
int pendingSoundData()
|
||||
{
|
||||
return audioData.size();
|
||||
};
|
||||
void stopSound();
|
||||
|
||||
// These are public so that AyaCefRenderer::OnPaint may modify them
|
||||
bool shouldReuploadTexture = false;
|
||||
std::vector<char> buffer;
|
||||
|
||||
Aya::remote_signal<void(bool)> isLoadedSignal;
|
||||
Aya::remote_signal<void(ChromiumMouseCursor)> mouseCursorChangedSignal;
|
||||
Aya::remote_signal<void(std::string, ChromiumConsoleMessageSeverity, std::string, int)> onConsoleMessageSignal;
|
||||
Aya::remote_signal<void(Vector2)> scrollOffsetChangedSignal;
|
||||
static Reflection::RemoteEventDesc<ChromiumFrame, void(std::string, ChromiumConsoleMessageSeverity, std::string, int)>
|
||||
event_ChromiumFrameConsoleMessage;
|
||||
|
||||
#if defined(ENABLE_CHROMIUM_FRAMES) && !defined(AYA_SERVER)
|
||||
// These are public so that the cefLoop may set them upon initialization
|
||||
CefRefPtr<CefBrowser> cefInstance;
|
||||
CefRefPtr<AyaCefRenderer> cefRenderer;
|
||||
CefRefPtr<AyaCefClient> cefClient;
|
||||
#endif
|
||||
|
||||
int lastPaintWidth;
|
||||
int lastPaintHeight;
|
||||
|
||||
private:
|
||||
#if defined(ENABLE_CHROMIUM_FRAMES) && !defined(AYA_SERVER)
|
||||
void mouseClick(int x, int y, cef_mouse_button_type_t button, bool isDown = true);
|
||||
#endif
|
||||
|
||||
static FMOD_RESULT F_CALLBACK AudioCallback(FMOD_SOUND* _sound, void* _data, unsigned int datalen);
|
||||
|
||||
std::string url;
|
||||
std::string pageTitle;
|
||||
|
||||
bool initialized;
|
||||
bool canPageBackward = false;
|
||||
bool canPageForward = false;
|
||||
bool worldAudioEnabled;
|
||||
bool playing = false;
|
||||
|
||||
int statusCode;
|
||||
float volume;
|
||||
float pitch;
|
||||
float worldAudioMinDistance;
|
||||
float worldAudioMaxDistance;
|
||||
Soundscape::RollOffMode worldAudioRollOff;
|
||||
float zoom = 0.0f;
|
||||
|
||||
Vector2 resolution;
|
||||
Vector2 mousePosition;
|
||||
Vector2 scrollOffset;
|
||||
|
||||
FMOD::Sound* fmod_sound;
|
||||
FMOD::Channel* fmod_channel; // the latest channel
|
||||
PartInstance* part; // The Part (if any) that this sound is attached to
|
||||
|
||||
ChromiumLoadState loadState;
|
||||
ChromiumMouseCursor mouseCursor;
|
||||
|
||||
shared_ptr<Graphics::Texture> texture;
|
||||
|
||||
protected:
|
||||
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
|
||||
/*override*/ void onAncestorChanged(const AncestorChanged& event);
|
||||
|
||||
//
|
||||
// IAdornable
|
||||
//
|
||||
|
||||
/*override*/ void render2d(Adorn* adorn);
|
||||
/*override*/ void renderBackground2d(Adorn* adorn);
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
215
engine/app/src/DataModel/ClickDetector.cpp
Normal file
215
engine/app/src/DataModel/ClickDetector.cpp
Normal file
@@ -0,0 +1,215 @@
|
||||
|
||||
|
||||
|
||||
#include "DataModel/ClickDetector.hpp"
|
||||
#include "DataModel/Workspace.hpp"
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
#include "Players.hpp"
|
||||
#include "Draw.hpp"
|
||||
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
const char* const sClickDetector = "ClickDetector";
|
||||
|
||||
|
||||
|
||||
static Reflection::RemoteEventDesc<ClickDetector, void(shared_ptr<Instance>)> desc_MouseClick(&ClickDetector::mouseClickSignal, "MouseClick",
|
||||
"playerWhoClicked", Security::None, Reflection::RemoteEventCommon::SCRIPTING, Reflection::RemoteEventCommon::BROADCAST);
|
||||
static Reflection::RemoteEventDesc<ClickDetector, void(shared_ptr<Instance>)> dep_MouseClick(&ClickDetector::mouseClickSignal, "mouseClick",
|
||||
"playerWhoClicked", Security::None,
|
||||
Reflection::RemoteEventCommon::Attributes::deprecated(Reflection::RemoteEventCommon::SCRIPTING, &desc_MouseClick),
|
||||
Reflection::RemoteEventCommon::BROADCAST);
|
||||
static Reflection::RemoteEventDesc<ClickDetector, void(shared_ptr<Instance>)> desc_RightMouseClick(&ClickDetector::rightMouseClickSignal,
|
||||
"RightMouseClick", "playerWhoClicked", Security::None, Reflection::RemoteEventCommon::SCRIPTING, Reflection::RemoteEventCommon::BROADCAST);
|
||||
static Reflection::RemoteEventDesc<ClickDetector, void(shared_ptr<Instance>)> desc_MouseHoverEnter(&ClickDetector::mouseHoverSignal,
|
||||
"MouseHoverEnter", "playerWhoHovered", Security::None, Reflection::RemoteEventCommon::SCRIPTING, Reflection::RemoteEventCommon::CLIENT_SERVER);
|
||||
static Reflection::RemoteEventDesc<ClickDetector, void(shared_ptr<Instance>)> desc_MouseHoverLeave(&ClickDetector::mouseHoverLeaveSignal,
|
||||
"MouseHoverLeave", "playerWhoHovered", Security::None, Reflection::RemoteEventCommon::SCRIPTING, Reflection::RemoteEventCommon::CLIENT_SERVER);
|
||||
|
||||
Reflection::BoundProp<float> ClickDetector::propMaxActivationDistance("MaxActivationDistance", category_Data, &ClickDetector::maxActivationDistance);
|
||||
Reflection::BoundProp<TextureId> ClickDetector::propCursorIcon("CursorIcon", category_Image, &ClickDetector::cursorIcon);
|
||||
REFLECTION_END();
|
||||
|
||||
|
||||
ClickDetector::ClickDetector()
|
||||
: cycle(0)
|
||||
, maxActivationDistance(32.0)
|
||||
{
|
||||
setName("ClickDetector");
|
||||
lastHoverPart = shared_ptr<PartInstance>();
|
||||
}
|
||||
|
||||
|
||||
bool containsClickDetector(Instance* instance)
|
||||
{
|
||||
for (size_t i = 0; i < instance->numChildren(); ++i)
|
||||
{
|
||||
Instance* child = instance->getChild(i);
|
||||
if (Instance::fastDynamicCast<ClickDetector>(child))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ancestorContainsClickDetector(Instance* instance)
|
||||
{
|
||||
AYAASSERT(instance);
|
||||
if (containsClickDetector(instance))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Instance* parent = instance->getParent())
|
||||
{
|
||||
if (!Instance::fastDynamicCast<Workspace>(parent))
|
||||
{ // bail out at workspace level to prevent going through all workspace children
|
||||
return ancestorContainsClickDetector(parent);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void renderClickDetector(shared_ptr<Aya::Instance> descendant, Adorn* adorn, int cycle)
|
||||
{
|
||||
PartInstance* part = Instance::fastDynamicCast<PartInstance>(descendant.get());
|
||||
if (part)
|
||||
{
|
||||
// Vector3 size = 0.5f * (part->getPartSizeXml() + 0.5 * Vector3::one()); // one stud bigger
|
||||
// AABox fieldBox(-size, size);
|
||||
// adorn->setObjectToWorldMatrix(part->getCoordinateFrame());
|
||||
// adorn->box(fieldBox, Color4(0.0, 0.0, 1.0, 0.5) );
|
||||
|
||||
int max = ClickDetector::cycles();
|
||||
int half = max / 2;
|
||||
|
||||
int value = cycle < half ? cycle : max - cycle;
|
||||
float percent = static_cast<float>(value) / static_cast<float>(half);
|
||||
|
||||
Color4 color(1.0f - percent, percent, 0.5f);
|
||||
|
||||
Draw::selectionBox(part->getPart(), adorn, color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ClickDetector::render3dAdorn(Adorn* adorn)
|
||||
{
|
||||
/*
|
||||
Instance* parent = this->getParent();
|
||||
|
||||
AYAASSERT(!dynamic_cast<Workspace*>(parent));
|
||||
|
||||
cycle = (cycle + 1) % cycles();
|
||||
|
||||
parent->visitDescendants(boost::bind(&renderClickDetector, _1, adorn, cycle));
|
||||
renderClickDetector(shared_from(parent), adorn, cycle);
|
||||
*/
|
||||
}
|
||||
|
||||
void ClickDetector::fireMouseClick(float distance, Aya::Network::Player* player)
|
||||
{
|
||||
if (distance < maxActivationDistance)
|
||||
desc_MouseClick.fireAndReplicateEvent(this, shared_from(player));
|
||||
}
|
||||
|
||||
void ClickDetector::fireRightMouseClick(float distance, Aya::Network::Player* player)
|
||||
{
|
||||
if (distance < maxActivationDistance)
|
||||
desc_RightMouseClick.fireAndReplicateEvent(this, shared_from(player));
|
||||
}
|
||||
|
||||
bool ClickDetector::isClickable(shared_ptr<PartInstance> part, float distanceToCharacter, bool raiseClickedEvent, Aya::Network::Player* player,
|
||||
TextureId** cursorIcon, bool isRightClick)
|
||||
{
|
||||
shared_ptr<Instance> clickDetectorParent = part;
|
||||
while (clickDetectorParent && fastSharedDynamicCast<Workspace>(clickDetectorParent) == NULL)
|
||||
{
|
||||
if (shared_ptr<ClickDetector> cd = shared_from(clickDetectorParent->findFirstChildOfType<ClickDetector>()))
|
||||
{
|
||||
**cursorIcon = cd->getCursorIcon();
|
||||
|
||||
if (distanceToCharacter < cd->getMaxActivationDistance())
|
||||
{
|
||||
if (raiseClickedEvent)
|
||||
{
|
||||
if (isRightClick)
|
||||
cd->fireRightMouseClick(distanceToCharacter, player);
|
||||
else
|
||||
cd->fireMouseClick(distanceToCharacter, player);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
clickDetectorParent = shared_from(clickDetectorParent->getParent());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ClickDetector::updateLastHoverPart(shared_ptr<Instance> newHover, Aya::Network::Player* player)
|
||||
{
|
||||
if (newHover != lastHoverPart)
|
||||
{
|
||||
if (newHover)
|
||||
fireMouseHover(player);
|
||||
lastHoverPart = newHover;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ClickDetector::fireMouseHover(Aya::Network::Player* player)
|
||||
{
|
||||
desc_MouseHoverEnter.fireAndReplicateEvent(this, shared_from(player));
|
||||
}
|
||||
|
||||
void ClickDetector::fireMouseHoverLeave(Aya::Network::Player* player)
|
||||
{
|
||||
desc_MouseHoverLeave.fireAndReplicateEvent(this, shared_from(player));
|
||||
lastHoverPart = shared_ptr<PartInstance>();
|
||||
}
|
||||
|
||||
void ClickDetector::stopHover(shared_ptr<PartInstance> part, Aya::Network::Player* player)
|
||||
{
|
||||
if (part && part.get())
|
||||
{
|
||||
if (Instance* instance = fastDynamicCast<Instance>(part.get()))
|
||||
{
|
||||
do
|
||||
{
|
||||
if (ClickDetector* cd = instance->findFirstChildOfType<ClickDetector>())
|
||||
cd->fireMouseHoverLeave(player);
|
||||
instance = instance->getParent();
|
||||
} while (instance != NULL && Instance::fastDynamicCast<Workspace>(instance) == NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool ClickDetector::isHovered(PartInstance* part, float distanceToCharacter, bool raiseHoveredEvent, Aya::Network::Player* player)
|
||||
{
|
||||
if (part)
|
||||
{
|
||||
shared_ptr<Instance> i = shared_from(part);
|
||||
do
|
||||
{
|
||||
if (shared_ptr<ClickDetector> cd = shared_from(i->findFirstChildOfType<ClickDetector>()))
|
||||
{
|
||||
if ((distanceToCharacter < cd->getMaxActivationDistance()) && cd)
|
||||
{
|
||||
return cd->updateLastHoverPart(i, player);
|
||||
}
|
||||
}
|
||||
i = shared_from(i->getParent());
|
||||
} while (i != NULL && Instance::fastDynamicCast<Workspace>(i.get()) == NULL);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
90
engine/app/src/DataModel/ClickDetector.hpp
Normal file
90
engine/app/src/DataModel/ClickDetector.hpp
Normal file
@@ -0,0 +1,90 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Tree/Instance.hpp"
|
||||
#include "Base/IAdornable.hpp"
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
#include "DataModel/ModelInstance.hpp"
|
||||
#include "Utility/TextureId.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Network
|
||||
{
|
||||
class Players;
|
||||
class Player;
|
||||
} // namespace Network
|
||||
|
||||
class PartInstance;
|
||||
class ModelInstance;
|
||||
|
||||
extern const char* const sClickDetector;
|
||||
class ClickDetector
|
||||
: public DescribedCreatable<ClickDetector, Instance, sClickDetector>
|
||||
, public IAdornable
|
||||
{
|
||||
private:
|
||||
int cycle;
|
||||
float maxActivationDistance; // max distance a character can be from the button and still raise events
|
||||
TextureId cursorIcon; // cursor icon that shows when the player hovers over this clickdetector
|
||||
shared_ptr<Instance> lastHoverPart;
|
||||
|
||||
// IAdornable
|
||||
/*override*/ bool shouldRender3dAdorn() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
/*override*/ void render3dAdorn(Adorn* adorn);
|
||||
|
||||
public:
|
||||
ClickDetector();
|
||||
virtual ~ClickDetector() {}
|
||||
|
||||
void fireMouseClick(float distance, Aya::Network::Player* player);
|
||||
void fireRightMouseClick(float distance, Aya::Network::Player* player);
|
||||
void fireMouseHover(Aya::Network::Player* player);
|
||||
void fireMouseHoverLeave(Aya::Network::Player* player);
|
||||
static Reflection::BoundProp<float> propMaxActivationDistance;
|
||||
static Reflection::BoundProp<TextureId> propCursorIcon;
|
||||
|
||||
shared_ptr<Instance> getLastHoverPart()
|
||||
{
|
||||
return lastHoverPart;
|
||||
}
|
||||
bool updateLastHoverPart(shared_ptr<Instance> newHover, Aya::Network::Player* player);
|
||||
|
||||
Aya::remote_signal<void(shared_ptr<Instance>)> mouseClickSignal;
|
||||
Aya::remote_signal<void(shared_ptr<Instance>)> rightMouseClickSignal;
|
||||
Aya::remote_signal<void(shared_ptr<Instance>)> mouseHoverSignal;
|
||||
Aya::remote_signal<void(shared_ptr<Instance>)> mouseHoverLeaveSignal;
|
||||
|
||||
static int cycles()
|
||||
{
|
||||
return 30;
|
||||
}
|
||||
|
||||
float getMaxActivationDistance()
|
||||
{
|
||||
return maxActivationDistance;
|
||||
}
|
||||
TextureId getCursorIcon()
|
||||
{
|
||||
return cursorIcon;
|
||||
}
|
||||
|
||||
|
||||
static bool isClickable(shared_ptr<PartInstance> part, float distanceToCharacter, bool raiseClickedEvent, Aya::Network::Player* player,
|
||||
TextureId** cursorIcon = NULL, bool isRightClick = false);
|
||||
static bool isHovered(PartInstance* part, float distanceToCharacter, bool raiseHoveredEvent, Aya::Network::Player* player);
|
||||
static void stopHover(shared_ptr<PartInstance> part, Aya::Network::Player* player);
|
||||
/* override */ bool askSetParent(const Instance* parent) const
|
||||
{
|
||||
return (Instance::fastDynamicCast<PartInstance>(parent) != NULL) || (Instance::fastDynamicCast<ModelInstance>(parent) != NULL);
|
||||
}
|
||||
/* override */ bool askAddChild(const Instance* instance) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
} // namespace Aya
|
||||
84
engine/app/src/DataModel/CollectionService.cpp
Normal file
84
engine/app/src/DataModel/CollectionService.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
|
||||
|
||||
#include "DataModel/CollectionService.hpp"
|
||||
#include "DataModel/Configuration.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
|
||||
const char* const sCollectionService = "CollectionService";
|
||||
|
||||
|
||||
static Reflection::BoundFuncDesc<CollectionService, shared_ptr<const Instances>(std::string)> func_GetCollection(
|
||||
&CollectionService::getCollection, "GetCollection", "class", Security::None);
|
||||
Reflection::EventDesc<CollectionService, void(shared_ptr<Instance>)> event_collectionItemAdded(
|
||||
&CollectionService::itemAddedSignal, "ItemAdded", "instance");
|
||||
Reflection::EventDesc<CollectionService, void(shared_ptr<Instance>)> event_collectionItemRemoved(
|
||||
&CollectionService::itemRemovedSignal, "ItemRemoved", "instance");
|
||||
REFLECTION_END();
|
||||
|
||||
|
||||
CollectionService::CollectionService()
|
||||
: DescribedNonCreatable<CollectionService, Instance, sCollectionService>(sCollectionService)
|
||||
{
|
||||
}
|
||||
|
||||
shared_ptr<const Instances> CollectionService::getCollection(const Name& className)
|
||||
{
|
||||
return getCollection(className.toString());
|
||||
}
|
||||
|
||||
shared_ptr<const Instances> CollectionService::getCollection(std::string className)
|
||||
{
|
||||
const CollectionMap::iterator iter = collections.find(className);
|
||||
if (iter != collections.end())
|
||||
{
|
||||
return iter->second->read();
|
||||
}
|
||||
return shared_ptr<const Instances>();
|
||||
}
|
||||
|
||||
void CollectionService::removeInstance(shared_ptr<Instance> instance)
|
||||
{
|
||||
const std::string& className = instance->getDescriptor().name.toString();
|
||||
|
||||
CollectionMap::iterator collectionIter = collections.find(className);
|
||||
|
||||
AYAASSERT(collectionIter != collections.end());
|
||||
Instances::iterator iter;
|
||||
shared_ptr<Instances> c(collectionIter->second->write());
|
||||
|
||||
iter = std::find(c->begin(), c->end(), instance);
|
||||
if (iter != c->end())
|
||||
{
|
||||
// Fast-remove. This can make a huge speed improvement over regular remove
|
||||
*iter = c->back();
|
||||
c->pop_back();
|
||||
|
||||
itemRemovedSignal(instance);
|
||||
}
|
||||
else
|
||||
{
|
||||
AYAASSERT(0);
|
||||
}
|
||||
}
|
||||
void CollectionService::addInstance(shared_ptr<Instance> instance)
|
||||
{
|
||||
const std::string& className = instance->getDescriptor().name.toString();
|
||||
|
||||
CollectionMap::iterator collectionIter = collections.find(className);
|
||||
if (collectionIter == collections.end())
|
||||
{
|
||||
collections[className].reset(new copy_on_write_ptr<Instances>());
|
||||
}
|
||||
|
||||
shared_ptr<Instances> c(collections[className]->write());
|
||||
c->push_back(instance);
|
||||
|
||||
itemAddedSignal(instance);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace Aya
|
||||
37
engine/app/src/DataModel/CollectionService.hpp
Normal file
37
engine/app/src/DataModel/CollectionService.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "Tree/Service.hpp"
|
||||
#include <queue>
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
extern const char* const sCollectionService;
|
||||
class CollectionService
|
||||
: public DescribedNonCreatable<CollectionService, Instance, sCollectionService>
|
||||
, public Service
|
||||
{
|
||||
public:
|
||||
CollectionService();
|
||||
|
||||
Aya::signal<void(shared_ptr<Instance>)> itemAddedSignal;
|
||||
Aya::signal<void(shared_ptr<Instance>)> itemRemovedSignal;
|
||||
|
||||
shared_ptr<const Instances> getCollection(std::string type);
|
||||
shared_ptr<const Instances> getCollection(const Name& className);
|
||||
template<class T>
|
||||
shared_ptr<const Instances> getCollection()
|
||||
{
|
||||
return getCollection(T::classDescriptor());
|
||||
}
|
||||
|
||||
void removeInstance(shared_ptr<Instance> instance);
|
||||
void addInstance(shared_ptr<Instance> instance);
|
||||
|
||||
private:
|
||||
// TODO: Lookup by const Aya::Name*
|
||||
typedef std::map<std::string, shared_ptr<copy_on_write_ptr<Instances>>> CollectionMap;
|
||||
CollectionMap collections;
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
63
engine/app/src/DataModel/ColorCorrectionEffect.cpp
Normal file
63
engine/app/src/DataModel/ColorCorrectionEffect.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#include "ColorCorrectionEffect.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
const char* const sColorCorrectionEffect = "ColorCorrectionEffect";
|
||||
|
||||
static Reflection::PropDescriptor<ColorCorrectionEffect, float> prop_brightness("Brightness", "State", &ColorCorrectionEffect::getBrightness, &ColorCorrectionEffect::setBrightness, Reflection::PropertyDescriptor::STANDARD);
|
||||
static Reflection::PropDescriptor<ColorCorrectionEffect, float> prop_contrast("Contrast", "State", &ColorCorrectionEffect::getContrast, &ColorCorrectionEffect::setContrast, Reflection::PropertyDescriptor::STANDARD);
|
||||
static Reflection::PropDescriptor<ColorCorrectionEffect, float> prop_saturation("Saturation", "State", &ColorCorrectionEffect::getSaturation, &ColorCorrectionEffect::setSaturation, Reflection::PropertyDescriptor::STANDARD);
|
||||
static Reflection::PropDescriptor<ColorCorrectionEffect, Color3> prop_tintColor("TintColor", "State", &ColorCorrectionEffect::getTintColor, &ColorCorrectionEffect::setTintColor, Reflection::PropertyDescriptor::STANDARD);
|
||||
|
||||
ColorCorrectionEffect::ColorCorrectionEffect()
|
||||
: m_brightness(0)
|
||||
, m_contrast(0)
|
||||
, m_saturation(0)
|
||||
, m_tintColor(Color3::white())
|
||||
{
|
||||
setName(sColorCorrectionEffect);
|
||||
}
|
||||
|
||||
ColorCorrectionEffect::~ColorCorrectionEffect()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
void ColorCorrectionEffect::setBrightness(float brightness)
|
||||
{
|
||||
if (m_brightness == brightness)
|
||||
return;
|
||||
|
||||
m_brightness = brightness;
|
||||
raisePropertyChanged(prop_brightness);
|
||||
}
|
||||
|
||||
void ColorCorrectionEffect::setContrast(float contrast)
|
||||
{
|
||||
if (m_contrast == contrast)
|
||||
return;
|
||||
|
||||
m_contrast = contrast;
|
||||
raisePropertyChanged(prop_contrast);
|
||||
}
|
||||
|
||||
void ColorCorrectionEffect::setSaturation(float saturation)
|
||||
{
|
||||
if (m_saturation == saturation)
|
||||
return;
|
||||
|
||||
m_saturation = -saturation; // NOTE: negative is intnetional, scenemanager treats this as grayscalelevel, too lazy to fix :P
|
||||
raisePropertyChanged(prop_saturation);
|
||||
}
|
||||
|
||||
void ColorCorrectionEffect::setTintColor(Color3 tint)
|
||||
{
|
||||
if (m_tintColor == tint)
|
||||
return;
|
||||
|
||||
m_tintColor = tint;
|
||||
raisePropertyChanged(prop_tintColor);
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
32
engine/app/src/DataModel/ColorCorrectionEffect.hpp
Normal file
32
engine/app/src/DataModel/ColorCorrectionEffect.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "PostEffect.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
extern const char* const sColorCorrectionEffect;
|
||||
|
||||
class ColorCorrectionEffect : public DescribedCreatable<ColorCorrectionEffect, PostEffect, sColorCorrectionEffect>
|
||||
{
|
||||
public:
|
||||
ColorCorrectionEffect();
|
||||
~ColorCorrectionEffect();
|
||||
|
||||
void setBrightness(float brightness);
|
||||
void setContrast(float contrast);
|
||||
void setSaturation(float saturation);
|
||||
void setTintColor(Color3 tint);
|
||||
|
||||
float getBrightness() const { return m_brightness; }
|
||||
float getContrast() const { return m_contrast; }
|
||||
float getSaturation() const { return m_saturation; }
|
||||
Color3 getTintColor() const { return m_tintColor; }
|
||||
private:
|
||||
float m_brightness = 0.0f;
|
||||
float m_contrast = 0.0f;
|
||||
float m_saturation = 0.0f;
|
||||
Color3 m_tintColor = Color3::white();
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
294
engine/app/src/DataModel/ColorSequence.cpp
Normal file
294
engine/app/src/DataModel/ColorSequence.cpp
Normal file
@@ -0,0 +1,294 @@
|
||||
#include "DataModel/ColorSequence.hpp"
|
||||
#include "g3dmath.hpp"
|
||||
#include "g3dmath.hpp"
|
||||
#include "Debug.hpp"
|
||||
#include "Utility/G3DCore.hpp"
|
||||
using G3D::clamp;
|
||||
using G3D::Color3;
|
||||
using G3D::lerp;
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
static inline Color3 clamp(Color3 v)
|
||||
{
|
||||
v.r = G3D::clamp(v.r, 0, 1);
|
||||
v.g = G3D::clamp(v.g, 0, 1);
|
||||
v.b = G3D::clamp(v.b, 0, 1);
|
||||
return v;
|
||||
}
|
||||
|
||||
ColorSequence::ColorSequence(Color3 constant)
|
||||
: m_data(2)
|
||||
{
|
||||
m_data[0] = Key(0, constant, 0);
|
||||
m_data[1] = Key(1, constant, 0);
|
||||
}
|
||||
|
||||
ColorSequence::ColorSequence(Color3 a, Color3 b)
|
||||
: m_data(2)
|
||||
{
|
||||
m_data[0] = Key(0, a, 0);
|
||||
m_data[1] = Key(1, b, 0);
|
||||
}
|
||||
|
||||
ColorSequence::ColorSequence(const std::vector<Key>& keys, bool exceptions)
|
||||
{
|
||||
if (!validate(keys, exceptions))
|
||||
{
|
||||
ColorSequence().m_data.swap(m_data);
|
||||
return;
|
||||
}
|
||||
m_data = keys;
|
||||
m_data.front().time = 0;
|
||||
m_data.back().time = 1;
|
||||
}
|
||||
|
||||
ColorSequence::ColorSequence(const ColorSequence& r)
|
||||
{
|
||||
AYAASSERT(validate(r.m_data, false));
|
||||
|
||||
m_data = r.m_data;
|
||||
for (unsigned j = 0, e = m_data.size(); j < e; ++j)
|
||||
{
|
||||
m_data[j].time = G3D::clamp(m_data[j].time, 0, 1);
|
||||
m_data[j].value = clamp(m_data[j].value);
|
||||
m_data[j].envelope = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ColorSequence::resample(G3D::Vector3* min, G3D::Vector3* max, int numPoints) const
|
||||
{
|
||||
// invariant violation: this should not happen under any circumstances:
|
||||
AYAASSERT(m_data.size() >= 2);
|
||||
AYAASSERT(m_data.front().time == 0.0f); // yup, straight equal
|
||||
AYAASSERT(m_data.back().time == 1.0f); // ditto
|
||||
|
||||
int src = 0;
|
||||
float t = 0, dt = 1.0f / (numPoints - 1.0f) - 1e-5f;
|
||||
const std::vector<Key>& data = m_data;
|
||||
|
||||
min[0] = max[0] = Vector3(data[0].value);
|
||||
|
||||
for (int j = 1; j < numPoints; ++j)
|
||||
{
|
||||
t += dt;
|
||||
AYAASSERT(data[src].time <= data[src + 1].time); // invariant violation
|
||||
while (m_data[src + 1].time < t)
|
||||
src++; // find the next key
|
||||
|
||||
float s = (t - data[src].time) / (data[src + 1].time - data[src].time);
|
||||
max[j] = min[j] = Vector3(lerp(data[src].value, data[src + 1].value, s));
|
||||
}
|
||||
}
|
||||
|
||||
bool ColorSequenceKeypoint::operator==(const ColorSequenceKeypoint& r) const
|
||||
{
|
||||
return 0 == memcmp(this, &r, sizeof(r));
|
||||
}
|
||||
|
||||
bool ColorSequence::operator==(const ColorSequence& r) const
|
||||
{
|
||||
return m_data == r.m_data;
|
||||
}
|
||||
|
||||
bool ColorSequence::validate(const std::vector<Key>& keys, bool exc)
|
||||
{
|
||||
if (keys.size() < 2)
|
||||
{
|
||||
if (exc)
|
||||
throw Aya::runtime_error("ColorSequence: requires at least 2 keypoints");
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
if (keys.size() > kMaxSize)
|
||||
{
|
||||
if (exc)
|
||||
throw Aya::runtime_error("NumberSequence: max number of keypoints exceeded.");
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned j = 0; j < keys.size(); ++j)
|
||||
{
|
||||
if (j < keys.size() - 1 && keys[j].time >= keys[j + 1].time)
|
||||
{
|
||||
if (exc)
|
||||
throw Aya::runtime_error("ColorSequence: all keypoints must be ordered by time");
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
if (keys[j].value.min() < 0 || keys[j].value.max() > 1)
|
||||
{
|
||||
if (exc)
|
||||
throw Aya::runtime_error("ColorSequence: color value out of range");
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (fabsf(keys.front().time) > 1e-4f)
|
||||
{
|
||||
if (exc)
|
||||
throw Aya::runtime_error("ColorSequence must start at time=0.0");
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fabsf(keys.back().time - 1.0f) > 1e-4f)
|
||||
{
|
||||
if (exc)
|
||||
throw Aya::runtime_error("ColorSequence must end at time=1.0");
|
||||
else
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// serialization support
|
||||
|
||||
static std::string tostr(const ColorSequence& cs)
|
||||
{
|
||||
const std::vector<ColorSequence::Key>& keys = cs.getPoints();
|
||||
std::ostringstream oss;
|
||||
for (int j = 0, e = keys.size(); j < e; ++j)
|
||||
{
|
||||
oss << keys[j].time << " " << keys[j].value.r << " " << keys[j].value.g << " " << keys[j].value.b << " " << keys[j].envelope << " ";
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
static ColorSequence fromstr(const std::string& str)
|
||||
{
|
||||
std::vector<ColorSequence::Key> keys;
|
||||
std::istringstream iss(str);
|
||||
while (true)
|
||||
{
|
||||
ColorSequence::Key k;
|
||||
iss >> k.time >> k.value.r >> k.value.g >> k.value.b >> k.envelope;
|
||||
if (iss.eof())
|
||||
break;
|
||||
keys.push_back(k);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
namespace Reflection
|
||||
{
|
||||
|
||||
template<>
|
||||
int TypedPropertyDescriptor<ColorSequence>::getDataSize(const DescribedBase* instance) const
|
||||
{
|
||||
return sizeof(ColorSequence::Key) * getValue(instance).getPoints().size();
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
void TypedPropertyDescriptor<ColorSequence>::readValue(DescribedBase* instance, const XmlElement* element, IReferenceBinder& binder) const
|
||||
{
|
||||
std::string nodeval;
|
||||
element->getValue(nodeval);
|
||||
setValue(instance, fromstr(nodeval));
|
||||
}
|
||||
|
||||
template<>
|
||||
void TypedPropertyDescriptor<ColorSequence>::writeValue(const DescribedBase* instance, XmlElement* element) const
|
||||
{
|
||||
std::string nodeval = tostr(getValue(instance));
|
||||
element->setValue(nodeval);
|
||||
}
|
||||
|
||||
template<>
|
||||
bool TypedPropertyDescriptor<ColorSequence>::hasStringValue() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
std::string TypedPropertyDescriptor<ColorSequence>::getStringValue(const DescribedBase* instance) const
|
||||
{
|
||||
ColorSequence s = getValue(instance);
|
||||
std::string text = tostr(s);
|
||||
return text;
|
||||
}
|
||||
|
||||
template<>
|
||||
bool TypedPropertyDescriptor<ColorSequence>::setStringValue(DescribedBase* instance, const std::string& text) const
|
||||
{
|
||||
ColorSequence s = fromstr(text);
|
||||
setValue(instance, s);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
ColorSequence& Variant::convert<ColorSequence>()
|
||||
{
|
||||
return genericConvert<ColorSequence>();
|
||||
}
|
||||
|
||||
template<>
|
||||
Type const& Type::getSingleton<ColorSequence>()
|
||||
{
|
||||
static TType<ColorSequence> type("ColorSequence");
|
||||
return type;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
template<>
|
||||
ColorSequenceKeypoint& Variant::convert<ColorSequenceKeypoint>()
|
||||
{
|
||||
return genericConvert<ColorSequenceKeypoint>();
|
||||
}
|
||||
template<>
|
||||
Type const& Type::getSingleton<ColorSequenceKeypoint>()
|
||||
{
|
||||
static TType<ColorSequenceKeypoint> type("ColorSequenceKeypoint");
|
||||
return type;
|
||||
}
|
||||
|
||||
} // namespace Reflection
|
||||
|
||||
|
||||
template<>
|
||||
bool StringConverter<ColorSequence>::convertToValue(const std::string& text, ColorSequence& value)
|
||||
{
|
||||
if (text.empty())
|
||||
return false;
|
||||
value = fromstr(text);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
std::string StringConverter<ColorSequence>::convertToString(const ColorSequence& value)
|
||||
{
|
||||
return tostr(value);
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
bool StringConverter<ColorSequenceKeypoint>::convertToValue(const std::string& text, ColorSequenceKeypoint& value)
|
||||
{
|
||||
if (text.empty())
|
||||
return false;
|
||||
|
||||
std::istringstream iss(text);
|
||||
iss >> value.time >> value.value.r >> value.value.g >> value.value.b >> value.envelope;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
std::string StringConverter<ColorSequenceKeypoint>::convertToString(const ColorSequenceKeypoint& value)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << value.time << " " << value.value.r << " " << value.value.g << " " << value.value.b << " " << value.envelope << " ";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace Aya
|
||||
74
engine/app/src/DataModel/ColorSequence.hpp
Normal file
74
engine/app/src/DataModel/ColorSequence.hpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#undef min
|
||||
#undef max
|
||||
#include "Utility/G3DCore.hpp"
|
||||
#include "Reflection/Reflection.hpp"
|
||||
using G3D::Color3;
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class ColorSequenceKeypoint
|
||||
{
|
||||
public:
|
||||
float time;
|
||||
Color3 value;
|
||||
float envelope;
|
||||
|
||||
ColorSequenceKeypoint()
|
||||
: time(0)
|
||||
, value(0, 0, 0)
|
||||
, envelope(0)
|
||||
{
|
||||
}
|
||||
ColorSequenceKeypoint(float t, Color3 v, float e)
|
||||
: time(t)
|
||||
, value(v)
|
||||
, envelope(e)
|
||||
{
|
||||
}
|
||||
bool operator==(const ColorSequenceKeypoint& r) const;
|
||||
};
|
||||
|
||||
class ColorSequence
|
||||
{
|
||||
public:
|
||||
typedef ColorSequenceKeypoint Key;
|
||||
|
||||
enum
|
||||
{
|
||||
kMaxSize = 20
|
||||
}; // max number of keypoints permitted
|
||||
|
||||
explicit ColorSequence(Color3 constant = Color3(1, 1, 1));
|
||||
ColorSequence(Color3 a, Color3 b);
|
||||
ColorSequence(const std::vector<Key>& keys, bool exceptions = false);
|
||||
ColorSequence(const ColorSequence& cs);
|
||||
|
||||
const std::vector<Key>& getPoints() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
Key start() const
|
||||
{
|
||||
return m_data.front();
|
||||
}
|
||||
Key end() const
|
||||
{
|
||||
return m_data.back();
|
||||
}
|
||||
void resample(G3D::Vector3* min, G3D::Vector3* max, int numPoints) const;
|
||||
|
||||
bool operator==(const ColorSequence& r) const;
|
||||
|
||||
static bool validate(const std::vector<Key>& keys, bool exceptions);
|
||||
|
||||
private:
|
||||
std::vector<Key> m_data;
|
||||
};
|
||||
|
||||
|
||||
} // namespace Aya
|
||||
1327
engine/app/src/DataModel/Commands.cpp
Normal file
1327
engine/app/src/DataModel/Commands.cpp
Normal file
File diff suppressed because it is too large
Load Diff
925
engine/app/src/DataModel/Commands.hpp
Normal file
925
engine/app/src/DataModel/Commands.hpp
Normal file
@@ -0,0 +1,925 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Tree/Verb.hpp"
|
||||
#include "Utility/RunStateOwner.hpp"
|
||||
#include "Tool/ToolsArrow.hpp"
|
||||
#include "DataModel/Workspace.hpp"
|
||||
#include "PartOperation.hpp"
|
||||
|
||||
DYNAMIC_FASTFLAG(UseRemoveTypeIDTricks)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
class PVInstance;
|
||||
class Workspace;
|
||||
class DataModel;
|
||||
class Camera;
|
||||
|
||||
// Utility function
|
||||
void AddChildToRoot(XmlElement* root, shared_ptr<Instance> wsi, const boost::function<bool(Instance*)>& isInScope, Aya::CreatorRole creatorRole);
|
||||
void AddSelectionToRoot(XmlElement* root, Selection* sel, Aya::CreatorRole creatorRole);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// BASE COMMAND TYPES
|
||||
//
|
||||
|
||||
class RunStateVerb : public Verb
|
||||
{
|
||||
protected:
|
||||
DataModel* dataModel;
|
||||
ServiceClient<RunService> runService;
|
||||
RunStateVerb(std::string name, DataModel* dataModel, bool blacklisted = false);
|
||||
void playActionSound();
|
||||
|
||||
public:
|
||||
virtual ~RunStateVerb();
|
||||
};
|
||||
|
||||
// Edit commands are only enabled when the workspace is at Frame 0
|
||||
// and when something is selected
|
||||
class EditSelectionVerb : public Verb
|
||||
{
|
||||
protected:
|
||||
shared_ptr<Workspace> workspace;
|
||||
ServiceClient<Selection> selection;
|
||||
DataModel* dataModel;
|
||||
EditSelectionVerb(std::string name, DataModel* dataModel);
|
||||
EditSelectionVerb(VerbContainer* container, std::string name, DataModel* dataModel);
|
||||
|
||||
public:
|
||||
virtual ~EditSelectionVerb();
|
||||
virtual bool isEnabled() const;
|
||||
};
|
||||
|
||||
// Toggles the value of a bool property
|
||||
class BoolPropertyVerb : public EditSelectionVerb
|
||||
{
|
||||
private:
|
||||
const Name& propertyName;
|
||||
|
||||
protected:
|
||||
BoolPropertyVerb(const std::string& name, DataModel* dataModel, const char* propertyName);
|
||||
/*override*/ void doIt(IDataState* dataState);
|
||||
/*override*/ bool isChecked() const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Camera commands - always enabled
|
||||
class CameraVerb : public Verb
|
||||
{
|
||||
protected:
|
||||
Workspace* workspace;
|
||||
Camera* getCamera();
|
||||
const Camera* getCamera() const;
|
||||
ServiceClient<Selection> selection;
|
||||
|
||||
public:
|
||||
CameraVerb(std::string name, Workspace* _workspace);
|
||||
virtual bool isEnabled() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
virtual void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// EDIT MENU - descend from RunFrameZero commands
|
||||
class DeleteBase : public EditSelectionVerb
|
||||
{
|
||||
private:
|
||||
bool rewardHopper;
|
||||
|
||||
protected:
|
||||
DeleteBase(VerbContainer* container, DataModel* dataModel, std::string name);
|
||||
DeleteBase(DataModel* dataModel, std::string name, bool rewardHopper = false);
|
||||
|
||||
public:
|
||||
virtual void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
class DeleteSelectionVerb : public DeleteBase
|
||||
{
|
||||
protected:
|
||||
DeleteSelectionVerb(VerbContainer* container, DataModel* dataModel, std::string name)
|
||||
: DeleteBase(container, dataModel, name)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
DeleteSelectionVerb(DataModel* dataModel)
|
||||
: DeleteBase(dataModel, "Delete")
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class PlayDeleteSelectionVerb : public DeleteBase
|
||||
{
|
||||
public:
|
||||
PlayDeleteSelectionVerb(DataModel* dataModel)
|
||||
: DeleteBase(dataModel, "PlayDelete", true)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class SelectAllCommand : public RunStateVerb
|
||||
{
|
||||
public:
|
||||
SelectAllCommand(DataModel* dataModel)
|
||||
: RunStateVerb("SelectAll", dataModel)
|
||||
{
|
||||
}
|
||||
virtual void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
class SelectChildrenVerb : public EditSelectionVerb
|
||||
{
|
||||
private:
|
||||
typedef EditSelectionVerb Super;
|
||||
|
||||
public:
|
||||
SelectChildrenVerb(DataModel* dataModel);
|
||||
virtual bool isEnabled() const;
|
||||
virtual void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
class SnapSelectionVerb : public EditSelectionVerb
|
||||
{
|
||||
private:
|
||||
typedef EditSelectionVerb Super;
|
||||
|
||||
public:
|
||||
SnapSelectionVerb(DataModel* dataModel);
|
||||
virtual bool isEnabled() const;
|
||||
virtual void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
class UnlockAllVerb : public RunStateVerb
|
||||
{
|
||||
public:
|
||||
UnlockAllVerb(DataModel* dataModel)
|
||||
: RunStateVerb("UnlockAll", dataModel)
|
||||
{
|
||||
}
|
||||
virtual void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// VIEW MENU
|
||||
//
|
||||
|
||||
class CameraTiltUpCommand : public CameraVerb
|
||||
{
|
||||
public:
|
||||
CameraTiltUpCommand(Workspace* workspace)
|
||||
: CameraVerb("CameraTiltUp", workspace)
|
||||
{
|
||||
}
|
||||
virtual bool isEnabled() const;
|
||||
virtual void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
class CameraTiltDownCommand : public CameraVerb
|
||||
{
|
||||
public:
|
||||
CameraTiltDownCommand(Workspace* workspace)
|
||||
: CameraVerb("CameraTiltDown", workspace)
|
||||
{
|
||||
}
|
||||
virtual bool isEnabled() const;
|
||||
virtual void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
class CameraPanLeftCommand : public CameraVerb
|
||||
{
|
||||
public:
|
||||
CameraPanLeftCommand(Workspace* workspace)
|
||||
: CameraVerb("CameraPanLeft", workspace)
|
||||
{
|
||||
}
|
||||
virtual bool isEnabled() const;
|
||||
virtual void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
class CameraPanRightCommand : public CameraVerb
|
||||
{
|
||||
public:
|
||||
CameraPanRightCommand(Workspace* workspace)
|
||||
: CameraVerb("CameraPanRight", workspace)
|
||||
{
|
||||
}
|
||||
virtual bool isEnabled() const;
|
||||
virtual void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
|
||||
class CameraZoomInCommand : public CameraVerb
|
||||
{
|
||||
public:
|
||||
CameraZoomInCommand(Workspace* workspace)
|
||||
: CameraVerb("CameraZoomIn", workspace)
|
||||
{
|
||||
}
|
||||
virtual bool isEnabled() const;
|
||||
virtual void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
class CameraZoomOutCommand : public CameraVerb
|
||||
{
|
||||
public:
|
||||
CameraZoomOutCommand(Workspace* workspace)
|
||||
: CameraVerb("CameraZoomOut", workspace)
|
||||
{
|
||||
}
|
||||
virtual bool isEnabled() const;
|
||||
virtual void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
|
||||
class CameraZoomExtentsCommand : public CameraVerb
|
||||
{
|
||||
public:
|
||||
CameraZoomExtentsCommand(Workspace* workspace);
|
||||
virtual bool isEnabled() const;
|
||||
virtual void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
class CameraCenterCommand : public CameraVerb
|
||||
{
|
||||
public:
|
||||
CameraCenterCommand(Workspace* workspace);
|
||||
virtual bool isEnabled() const;
|
||||
virtual void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
|
||||
|
||||
class FirstPersonCommand : public Verb
|
||||
{
|
||||
private:
|
||||
DataModel* dataModel;
|
||||
|
||||
public:
|
||||
FirstPersonCommand(DataModel* dataModel);
|
||||
/*override*/ bool isEnabled() const;
|
||||
/*override*/ void doIt(IDataState* dataState) {}
|
||||
};
|
||||
|
||||
class ToggleViewMode : public Verb
|
||||
{
|
||||
private:
|
||||
DataModel* dataModel;
|
||||
|
||||
public:
|
||||
ToggleViewMode(Aya::DataModel* dm);
|
||||
|
||||
/*override*/ bool isChecked() const;
|
||||
/*override*/ bool isEnabled() const;
|
||||
/*override*/ bool isSelected() const;
|
||||
/*override*/ void doIt(Aya::IDataState* dataState);
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
class StatsCommand : public Verb
|
||||
{
|
||||
protected:
|
||||
DataModel* dataModel;
|
||||
|
||||
public:
|
||||
StatsCommand(DataModel* dataModel);
|
||||
/*override*/ bool isEnabled() const;
|
||||
/*override*/ bool isChecked() const;
|
||||
/*override*/ void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
class RenderStatsCommand : public Verb
|
||||
{
|
||||
protected:
|
||||
DataModel* dataModel;
|
||||
|
||||
public:
|
||||
RenderStatsCommand(DataModel* dataModel);
|
||||
/*override*/ bool isEnabled() const;
|
||||
/*override*/ bool isChecked() const;
|
||||
/*override*/ void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
class SummaryStatsCommand : public Verb
|
||||
{
|
||||
protected:
|
||||
DataModel* dataModel;
|
||||
|
||||
public:
|
||||
SummaryStatsCommand(DataModel* dataModel);
|
||||
/*override*/ bool isEnabled() const;
|
||||
/*override*/ bool isChecked() const;
|
||||
/*override*/ void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
class CustomStatsCommand : public Verb
|
||||
{
|
||||
protected:
|
||||
DataModel* dataModel;
|
||||
|
||||
public:
|
||||
CustomStatsCommand(DataModel* dataModel);
|
||||
/*override*/ bool isEnabled() const;
|
||||
/*override*/ bool isChecked() const;
|
||||
/*override*/ void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
class NetworkStatsCommand : public Verb
|
||||
{
|
||||
protected:
|
||||
DataModel* dataModel;
|
||||
|
||||
public:
|
||||
NetworkStatsCommand(DataModel* dataModel);
|
||||
/*override*/ bool isEnabled() const;
|
||||
/*override*/ bool isChecked() const;
|
||||
/*override*/ void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
class PhysicsStatsCommand : public Verb
|
||||
{
|
||||
protected:
|
||||
DataModel* dataModel;
|
||||
|
||||
public:
|
||||
PhysicsStatsCommand(DataModel* dataModel);
|
||||
/*override*/ bool isEnabled() const;
|
||||
/*override*/ bool isChecked() const;
|
||||
/*override*/ void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
|
||||
class EngineStatsCommand : public Verb
|
||||
{
|
||||
private:
|
||||
DataModel* dataModel;
|
||||
|
||||
public:
|
||||
EngineStatsCommand(DataModel* dataModel);
|
||||
/*override*/ bool isEnabled() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
/*override*/ void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
|
||||
|
||||
class JoinCommand : public Verb
|
||||
{
|
||||
protected:
|
||||
DataModel* dataModel;
|
||||
|
||||
public:
|
||||
JoinCommand(DataModel* dataModel);
|
||||
/*override*/ bool isEnabled() const;
|
||||
/*override*/ void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
|
||||
class ChatMenuCommand : public Verb
|
||||
{
|
||||
private:
|
||||
int menu1;
|
||||
int menu2;
|
||||
int menu3;
|
||||
|
||||
public:
|
||||
ChatMenuCommand(DataModel* dataModel, int menu1, int menu2, int menu3);
|
||||
/*override*/ void doIt(IDataState* dataState) {}
|
||||
|
||||
static std::string getChatString(int menu1, int menu2, int menu3);
|
||||
};
|
||||
|
||||
|
||||
class MouseCommand;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TOOL Launchers
|
||||
|
||||
|
||||
template<class MouseCommandClass, class ParentClass = RunStateVerb>
|
||||
class TToolVerb : public ParentClass
|
||||
{
|
||||
private:
|
||||
bool toggle;
|
||||
|
||||
public:
|
||||
TToolVerb(DataModel* dataModel, bool toggle = true, bool blacklisted = true)
|
||||
: ParentClass(MouseCommandClass::name().toString() +
|
||||
"Tool", // MouseCommand should be a Aya::Named<> descendant, or must implement a static name() function
|
||||
dataModel, blacklisted)
|
||||
, toggle(toggle)
|
||||
{
|
||||
}
|
||||
bool sameType(MouseCommand* mouseCommand) const
|
||||
{
|
||||
if (DFFlag::UseRemoveTypeIDTricks)
|
||||
{
|
||||
return mouseCommand ? MouseCommandClass::name() == mouseCommand->getName() : false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return mouseCommand ? typeid(MouseCommandClass) == typeid(*mouseCommand) : false;
|
||||
}
|
||||
}
|
||||
void doIt(IDataState* dataState)
|
||||
{
|
||||
if (isChecked() && toggle)
|
||||
{
|
||||
if (!MouseCommand::isAdvArrowToolEnabled())
|
||||
ParentClass::dataModel->getWorkspace()->setNullMouseCommand(); // Toggle on / off if already on
|
||||
else
|
||||
ParentClass::dataModel->getWorkspace()->setDefaultMouseCommand(); // Toggle on / off if already on
|
||||
}
|
||||
else
|
||||
{
|
||||
ParentClass::dataModel->getWorkspace()->setMouseCommand(newMouseCommand());
|
||||
}
|
||||
}
|
||||
bool isChecked() const
|
||||
{
|
||||
return sameType(ParentClass::dataModel->getWorkspace()->getCurrentMouseCommand());
|
||||
}
|
||||
virtual shared_ptr<MouseCommand> newMouseCommand()
|
||||
{
|
||||
return Creatable<MouseCommand>::create<MouseCommandClass>(ParentClass::dataModel->getWorkspace());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FORMAT MENU
|
||||
|
||||
class AnchorVerb : public EditSelectionVerb
|
||||
{
|
||||
public:
|
||||
AnchorVerb(DataModel* dataModel);
|
||||
virtual void doIt(IDataState* dataState);
|
||||
virtual bool isChecked() const;
|
||||
|
||||
private:
|
||||
FilteredSelection<Instance>* m_selection;
|
||||
};
|
||||
|
||||
class MaterialVerb : public EditSelectionVerb
|
||||
{
|
||||
public:
|
||||
MaterialVerb(DataModel* dataModel, std::string name = "MaterialVerb")
|
||||
: EditSelectionVerb(name, dataModel)
|
||||
{
|
||||
}
|
||||
virtual void doIt(IDataState* dataState);
|
||||
|
||||
static PartMaterial getCurrentMaterial()
|
||||
{
|
||||
return m_currentMaterial;
|
||||
}
|
||||
static void setCurrentMaterial(PartMaterial val)
|
||||
{
|
||||
m_currentMaterial = val;
|
||||
}
|
||||
static PartMaterial parseMaterial(const std::string materialString);
|
||||
|
||||
private:
|
||||
static PartMaterial m_currentMaterial;
|
||||
};
|
||||
|
||||
class ColorVerb : public EditSelectionVerb
|
||||
{
|
||||
public:
|
||||
ColorVerb(DataModel* dataModel, std::string name = "ColorVerb")
|
||||
: EditSelectionVerb(name, dataModel)
|
||||
{
|
||||
}
|
||||
virtual void doIt(IDataState* dataState);
|
||||
|
||||
static Aya::BrickColor getCurrentColor()
|
||||
{
|
||||
return m_currentColor;
|
||||
}
|
||||
static void setCurrentColor(Aya::BrickColor val)
|
||||
{
|
||||
m_currentColor = val;
|
||||
}
|
||||
|
||||
private:
|
||||
static Aya::BrickColor m_currentColor;
|
||||
};
|
||||
|
||||
|
||||
class TranslucentVerb : public BoolPropertyVerb
|
||||
{
|
||||
public:
|
||||
TranslucentVerb(DataModel* dataModel)
|
||||
: BoolPropertyVerb("TranslucentVerb", dataModel, "Transparent")
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class CanCollideVerb : public BoolPropertyVerb
|
||||
{
|
||||
public:
|
||||
CanCollideVerb(DataModel* dataModel)
|
||||
: BoolPropertyVerb("CanCollideVerb", dataModel, "CanCollide")
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class AllCanSelectCommand : public RunStateVerb
|
||||
{
|
||||
public:
|
||||
AllCanSelectCommand(DataModel* dataModel)
|
||||
: RunStateVerb("AllCanSelect", dataModel)
|
||||
{
|
||||
}
|
||||
virtual void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
class CanNotSelectCommand : public EditSelectionVerb
|
||||
{
|
||||
public:
|
||||
CanNotSelectCommand(DataModel* dataModel)
|
||||
: EditSelectionVerb("CanNotSelect", dataModel)
|
||||
{
|
||||
}
|
||||
virtual void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
|
||||
class MoveUpSelectionVerb : public EditSelectionVerb
|
||||
{
|
||||
private:
|
||||
float moveUpHeight;
|
||||
|
||||
public:
|
||||
MoveUpSelectionVerb(DataModel* dataModel, const std::string& name, float moveUpHeight)
|
||||
: EditSelectionVerb(name, dataModel)
|
||||
, moveUpHeight(moveUpHeight)
|
||||
{
|
||||
}
|
||||
/*override*/ void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
class MoveUpPlateVerb : public MoveUpSelectionVerb
|
||||
{
|
||||
public:
|
||||
MoveUpPlateVerb(DataModel* dataModel)
|
||||
: MoveUpSelectionVerb(dataModel, "SelectionUpPlate", 0.4f)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class MoveUpBrickVerb : public MoveUpSelectionVerb
|
||||
{
|
||||
public:
|
||||
MoveUpBrickVerb(DataModel* dataModel)
|
||||
: MoveUpSelectionVerb(dataModel, "SelectionUpBrick", 1.2f)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class MoveDownSelectionVerb : public EditSelectionVerb
|
||||
{
|
||||
public:
|
||||
MoveDownSelectionVerb(DataModel* dataModel);
|
||||
virtual void doIt(IDataState* dataState);
|
||||
};
|
||||
class RotateAxisCommand : public EditSelectionVerb
|
||||
{
|
||||
protected:
|
||||
RotateAxisCommand(std::string name, DataModel* dataModel)
|
||||
: EditSelectionVerb(name, dataModel)
|
||||
{
|
||||
}
|
||||
void rotateAboutAxis(const G3D::Matrix3& rotMatrix, const std::vector<PVInstance*>& selectedInstances);
|
||||
virtual G3D::Matrix3 getRotationAxis() = 0;
|
||||
|
||||
public:
|
||||
virtual void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
class RotateSelectionVerb : public RotateAxisCommand
|
||||
{
|
||||
public:
|
||||
RotateSelectionVerb(DataModel* dataModel);
|
||||
|
||||
protected:
|
||||
virtual G3D::Matrix3 getRotationAxis();
|
||||
};
|
||||
class TiltSelectionVerb : public RotateAxisCommand
|
||||
{
|
||||
public:
|
||||
TiltSelectionVerb(DataModel* dataModel);
|
||||
|
||||
protected:
|
||||
virtual G3D::Matrix3 getRotationAxis();
|
||||
};
|
||||
|
||||
|
||||
class CharacterCommand : public EditSelectionVerb
|
||||
{
|
||||
protected:
|
||||
CharacterCommand(const std::string& name, DataModel* dataModel)
|
||||
: EditSelectionVerb(name, dataModel)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
virtual bool isEnabled() const;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// RUN MENU
|
||||
//
|
||||
class RunCommand : public RunStateVerb
|
||||
{
|
||||
public:
|
||||
RunCommand(DataModel* dataModel)
|
||||
: RunStateVerb("Run", dataModel)
|
||||
{
|
||||
}
|
||||
virtual bool isEnabled() const;
|
||||
virtual void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
class StopCommand : public RunStateVerb
|
||||
{
|
||||
public:
|
||||
StopCommand(DataModel* dataModel)
|
||||
: RunStateVerb("Stop", dataModel)
|
||||
{
|
||||
}
|
||||
virtual bool isEnabled() const;
|
||||
virtual void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
class ResetCommand : public RunStateVerb
|
||||
{
|
||||
public:
|
||||
ResetCommand(DataModel* dataModel)
|
||||
: RunStateVerb("Reset", dataModel)
|
||||
{
|
||||
}
|
||||
virtual bool isEnabled() const;
|
||||
virtual void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Advanced Build Related
|
||||
//
|
||||
|
||||
class TurnOnManualJointCreation : public Verb
|
||||
{
|
||||
private:
|
||||
DataModel* dataModel;
|
||||
|
||||
public:
|
||||
TurnOnManualJointCreation(DataModel* dataModel);
|
||||
virtual bool isEnabled() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
virtual bool isChecked() const
|
||||
{
|
||||
return (AdvArrowTool::advCreateJointsMode && AdvArrowTool::advManualJointMode);
|
||||
}
|
||||
virtual void doIt(IDataState* dataState);
|
||||
};
|
||||
|
||||
class SetDragGridToOne : public Verb
|
||||
{
|
||||
private:
|
||||
DataModel* dataModel;
|
||||
|
||||
public:
|
||||
SetDragGridToOne(DataModel* dataModel);
|
||||
virtual bool isEnabled() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
virtual bool isChecked() const
|
||||
{
|
||||
return AdvArrowTool::advGridMode == DRAG::ONE_STUD;
|
||||
}
|
||||
virtual void doIt(IDataState* dataState)
|
||||
{
|
||||
AdvArrowTool::advGridMode = DRAG::ONE_STUD;
|
||||
}
|
||||
};
|
||||
|
||||
class SetDragGridToOneFifth : public Verb
|
||||
{
|
||||
private:
|
||||
DataModel* dataModel;
|
||||
|
||||
public:
|
||||
SetDragGridToOneFifth(DataModel* dataModel);
|
||||
virtual bool isEnabled() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
virtual bool isChecked() const
|
||||
{
|
||||
return AdvArrowTool::advGridMode == DRAG::QUARTER_STUD;
|
||||
}
|
||||
virtual void doIt(IDataState* dataState)
|
||||
{
|
||||
AdvArrowTool::advGridMode = DRAG::QUARTER_STUD;
|
||||
}
|
||||
};
|
||||
|
||||
class SetDragGridToOff : public Verb
|
||||
{
|
||||
private:
|
||||
DataModel* dataModel;
|
||||
|
||||
public:
|
||||
SetDragGridToOff(DataModel* dataModel);
|
||||
virtual bool isEnabled() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
virtual bool isChecked() const
|
||||
{
|
||||
return AdvArrowTool::advGridMode == DRAG::OFF;
|
||||
}
|
||||
virtual void doIt(IDataState* dataState)
|
||||
{
|
||||
AdvArrowTool::advGridMode = DRAG::OFF;
|
||||
}
|
||||
};
|
||||
|
||||
class SetGridSizeToTwo : public Verb
|
||||
{
|
||||
private:
|
||||
Workspace* workspace;
|
||||
|
||||
public:
|
||||
SetGridSizeToTwo(DataModel* dataModel);
|
||||
virtual bool isEnabled() const
|
||||
{
|
||||
return workspace->getShow3DGrid();
|
||||
}
|
||||
virtual bool isChecked() const
|
||||
{
|
||||
return (int)Workspace::gridSizeModifier == 2;
|
||||
}
|
||||
virtual void doIt(IDataState* dataState)
|
||||
{
|
||||
Workspace::gridSizeModifier = 2.0f;
|
||||
}
|
||||
};
|
||||
|
||||
class SetGridSizeToFour : public Verb
|
||||
{
|
||||
private:
|
||||
Workspace* workspace;
|
||||
|
||||
public:
|
||||
SetGridSizeToFour(DataModel* dataModel);
|
||||
virtual bool isEnabled() const
|
||||
{
|
||||
return workspace->getShow3DGrid();
|
||||
}
|
||||
virtual bool isChecked() const
|
||||
{
|
||||
return (int)Workspace::gridSizeModifier == 4;
|
||||
}
|
||||
virtual void doIt(IDataState* dataState)
|
||||
{
|
||||
Workspace::gridSizeModifier = 4.0f;
|
||||
}
|
||||
};
|
||||
|
||||
class SetGridSizeToSixteen : public Verb
|
||||
{
|
||||
private:
|
||||
Workspace* workspace;
|
||||
|
||||
public:
|
||||
SetGridSizeToSixteen(DataModel* dataModel);
|
||||
virtual bool isEnabled() const
|
||||
{
|
||||
return workspace->getShow3DGrid();
|
||||
}
|
||||
virtual bool isChecked() const
|
||||
{
|
||||
return (int)Workspace::gridSizeModifier == 16;
|
||||
}
|
||||
virtual void doIt(IDataState* dataState)
|
||||
{
|
||||
Workspace::gridSizeModifier = 16.0f;
|
||||
}
|
||||
};
|
||||
|
||||
class SetManualJointToWeak : public Verb
|
||||
{
|
||||
private:
|
||||
DataModel* dataModel;
|
||||
|
||||
public:
|
||||
SetManualJointToWeak(DataModel* dataModel);
|
||||
virtual bool isEnabled() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool isChecked() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual void doIt(IDataState* dataState)
|
||||
{
|
||||
AdvArrowTool::advManualJointType = DRAG::WEAK_MANUAL_JOINT;
|
||||
}
|
||||
};
|
||||
|
||||
class SetManualJointToStrong : public Verb
|
||||
{
|
||||
private:
|
||||
DataModel* dataModel;
|
||||
|
||||
public:
|
||||
SetManualJointToStrong(DataModel* dataModel);
|
||||
virtual bool isEnabled() const
|
||||
{
|
||||
return AdvArrowTool::advManualJointMode;
|
||||
}
|
||||
virtual bool isChecked() const
|
||||
{
|
||||
return AdvArrowTool::advManualJointType == DRAG::STRONG_MANUAL_JOINT;
|
||||
}
|
||||
virtual void doIt(IDataState* dataState)
|
||||
{
|
||||
AdvArrowTool::advManualJointType = DRAG::STRONG_MANUAL_JOINT;
|
||||
}
|
||||
};
|
||||
|
||||
class SetManualJointToInfinite : public Verb
|
||||
{
|
||||
private:
|
||||
DataModel* dataModel;
|
||||
|
||||
public:
|
||||
SetManualJointToInfinite(DataModel* dataModel);
|
||||
virtual bool isEnabled() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool isChecked() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual void doIt(IDataState* dataState)
|
||||
{
|
||||
AdvArrowTool::advManualJointType = DRAG::INFINITE_MANUAL_JOINT;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // namespace Aya
|
||||
120
engine/app/src/DataModel/CommonVerbs.cpp
Normal file
120
engine/app/src/DataModel/CommonVerbs.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
|
||||
|
||||
|
||||
#include "DataModel/CommonVerbs.hpp" // TODO - minimize these includes, and in the .h file
|
||||
#include "DataModel/Workspace.hpp"
|
||||
#include "DataModel/Camera.hpp"
|
||||
#include "DataModel/UserController.hpp"
|
||||
#include "DataModel/JointsService.hpp"
|
||||
#include "DataModel/Sky.hpp"
|
||||
#include "DataModel/Lighting.hpp"
|
||||
#include "DataModel/Teams.hpp"
|
||||
#include "DataModel/Hopper.hpp"
|
||||
#include "DataModel/Backpack.hpp"
|
||||
#include "DataModel/Tool.hpp"
|
||||
|
||||
#include "Humanoid/Humanoid.hpp"
|
||||
#include "Player.hpp"
|
||||
#include "Players.hpp"
|
||||
|
||||
#include "Tree/Verb.hpp"
|
||||
#include "World/World.hpp"
|
||||
#include "Kernel/Kernel.hpp"
|
||||
#include "GUI/GUI.hpp"
|
||||
#include "Script/LuaInstanceBridge.hpp"
|
||||
#include "Script/ScriptContext.hpp"
|
||||
#include "Utility/SoundWorld.hpp"
|
||||
#include "Utility/Sound.hpp"
|
||||
#include "Utility/StandardOut.hpp"
|
||||
#include "Log.hpp"
|
||||
#include "DrawPrimitives.hpp"
|
||||
#include "Utility/Http.hpp"
|
||||
|
||||
#include "format.hpp"
|
||||
|
||||
#include "Xml/Serializer.hpp"
|
||||
#include "boost/cast.hpp"
|
||||
#include "boost/scoped_ptr.hpp"
|
||||
|
||||
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
CommonVerbs::CommonVerbs(DataModel* dataModel)
|
||||
: joinCommand(dataModel)
|
||||
, statsCommand(dataModel)
|
||||
, renderStatsCommand(dataModel)
|
||||
, engineStatsCommand(dataModel)
|
||||
, networkStatsCommand(dataModel)
|
||||
, physicsStatsCommand(dataModel)
|
||||
, summaryStatsCommand(dataModel)
|
||||
, customStatsCommand(dataModel)
|
||||
, runCommand(dataModel)
|
||||
, stopCommand(dataModel)
|
||||
, resetCommand(dataModel)
|
||||
, axisRotateToolVerb(dataModel)
|
||||
, resizeToolVerb(dataModel)
|
||||
, advMoveToolVerb(dataModel)
|
||||
, advRotateToolVerb(dataModel)
|
||||
, advArrowToolVerb(dataModel, false)
|
||||
, turnOnManualJointCreationVerb(dataModel)
|
||||
, setDragGridToOneVerb(dataModel)
|
||||
, setDragGridToOneFifthVerb(dataModel)
|
||||
, setDragGridToOffVerb(dataModel)
|
||||
, setGridSizeToTwoVerb(dataModel)
|
||||
, setGridSizeToFourVerb(dataModel)
|
||||
, setGridSizeToSixteenVerb(dataModel)
|
||||
, setManualJointToWeakVerb(dataModel)
|
||||
, setManualJointToStrongVerb(dataModel)
|
||||
, setManualJointToInfiniteVerb(dataModel)
|
||||
,
|
||||
|
||||
flatToolVerb(dataModel)
|
||||
, glueToolVerb(dataModel)
|
||||
, weldToolVerb(dataModel)
|
||||
, studsToolVerb(dataModel)
|
||||
, inletToolVerb(dataModel)
|
||||
, universalToolVerb(dataModel)
|
||||
, hingeToolVerb(dataModel)
|
||||
, rightMotorToolVerb(dataModel)
|
||||
, leftMotorToolVerb(dataModel)
|
||||
, oscillateMotorToolVerb(dataModel)
|
||||
, smoothNoOutlinesToolVerb(dataModel)
|
||||
,
|
||||
|
||||
anchorToolVerb(dataModel)
|
||||
, lockToolVerb(dataModel)
|
||||
, fillToolVerb(dataModel, false)
|
||||
, materialToolVerb(dataModel, false)
|
||||
, materialVerb(dataModel)
|
||||
, colorVerb(dataModel)
|
||||
, anchorVerb(dataModel)
|
||||
, dropperToolVerb(dataModel)
|
||||
,
|
||||
|
||||
firstPersonCommand(dataModel)
|
||||
, selectChildrenVerb(dataModel)
|
||||
, snapSelectionVerb(dataModel)
|
||||
, playDeleteSelectionVerb(dataModel)
|
||||
, deleteSelectionVerb(dataModel)
|
||||
, moveUpPlateVerb(dataModel)
|
||||
, moveUpBrickVerb(dataModel)
|
||||
, moveDownSelectionVerb(dataModel)
|
||||
, rotateSelectionVerb(dataModel)
|
||||
, tiltSelectionVerb(dataModel)
|
||||
, selectAllCommand(dataModel)
|
||||
, allCanSelectCommand(dataModel)
|
||||
, canNotSelectCommand(dataModel)
|
||||
, translucentVerb(dataModel)
|
||||
, canCollideVerb(dataModel)
|
||||
, unlockAllVerb(dataModel)
|
||||
, gameToolVerb(dataModel)
|
||||
, grabToolVerb(dataModel)
|
||||
, cloneToolVerb(dataModel)
|
||||
, hammerToolVerb(dataModel)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
} // namespace Aya
|
||||
141
engine/app/src/DataModel/CommonVerbs.hpp
Normal file
141
engine/app/src/DataModel/CommonVerbs.hpp
Normal file
@@ -0,0 +1,141 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DataModel/DataModel.hpp"
|
||||
#include "DataModel/Commands.hpp"
|
||||
#include "DataModel/ToolsPart.hpp"
|
||||
#include "DataModel/ToolsSurface.hpp"
|
||||
#include "DataModel/ToolsModel.hpp"
|
||||
#include "Tool/ToolsArrow.hpp"
|
||||
#include "Tool/ResizeTool.hpp"
|
||||
#include "Tool/HammerTool.hpp"
|
||||
#include "Tool/GrabTool.hpp"
|
||||
#include "Tool/CloneTool.hpp"
|
||||
#include "Tool/NullTool.hpp"
|
||||
#include "Tool/GameTool.hpp"
|
||||
#include "Tool/AxisMoveTool.hpp"
|
||||
#include "Tool/AxisRotateTool.hpp"
|
||||
#include "Tool/MoveResizeJoinTool.hpp"
|
||||
#include "Tool/AdvMoveTool.hpp"
|
||||
#include "Tool/AdvRotateTool.hpp"
|
||||
#include "DataModel/UndoRedo.hpp"
|
||||
#include "DataModel/InputObject.hpp"
|
||||
#include "Tree/Instance.hpp"
|
||||
#include "Tree/Service.hpp"
|
||||
#include "Utility/RunStateOwner.hpp"
|
||||
|
||||
#include "Utility/InsertMode.hpp"
|
||||
|
||||
class XmlElement;
|
||||
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class Fonts;
|
||||
class GuiRoot;
|
||||
class GuiItem;
|
||||
class ContentProvider;
|
||||
class TimeState;
|
||||
class Hopper;
|
||||
class PlayerHopper;
|
||||
class StarterPackService;
|
||||
class Adorn;
|
||||
|
||||
// Contain a set of verbs used by Roblox
|
||||
class CommonVerbs
|
||||
{
|
||||
public:
|
||||
CommonVerbs(DataModel* dataModel);
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Play Mode Commands
|
||||
PlayDeleteSelectionVerb playDeleteSelectionVerb;
|
||||
|
||||
// Edit Menu
|
||||
DeleteSelectionVerb deleteSelectionVerb;
|
||||
SelectAllCommand selectAllCommand;
|
||||
SelectChildrenVerb selectChildrenVerb;
|
||||
SnapSelectionVerb snapSelectionVerb;
|
||||
UnlockAllVerb unlockAllVerb;
|
||||
ColorVerb colorVerb;
|
||||
MaterialVerb materialVerb;
|
||||
|
||||
// Format Menu
|
||||
AnchorVerb anchorVerb;
|
||||
TranslucentVerb translucentVerb;
|
||||
CanCollideVerb canCollideVerb;
|
||||
CanNotSelectCommand canNotSelectCommand;
|
||||
AllCanSelectCommand allCanSelectCommand;
|
||||
MoveUpPlateVerb moveUpPlateVerb;
|
||||
MoveUpBrickVerb moveUpBrickVerb;
|
||||
MoveDownSelectionVerb moveDownSelectionVerb;
|
||||
RotateSelectionVerb rotateSelectionVerb;
|
||||
TiltSelectionVerb tiltSelectionVerb;
|
||||
|
||||
// Run Menu
|
||||
RunCommand runCommand;
|
||||
StopCommand stopCommand;
|
||||
ResetCommand resetCommand;
|
||||
|
||||
// Test Menu
|
||||
FirstPersonCommand firstPersonCommand;
|
||||
StatsCommand statsCommand;
|
||||
RenderStatsCommand renderStatsCommand;
|
||||
EngineStatsCommand engineStatsCommand;
|
||||
NetworkStatsCommand networkStatsCommand;
|
||||
PhysicsStatsCommand physicsStatsCommand;
|
||||
SummaryStatsCommand summaryStatsCommand;
|
||||
CustomStatsCommand customStatsCommand;
|
||||
JoinCommand joinCommand;
|
||||
|
||||
// Adv Build Related
|
||||
TurnOnManualJointCreation turnOnManualJointCreationVerb;
|
||||
|
||||
SetDragGridToOne setDragGridToOneVerb;
|
||||
SetDragGridToOneFifth setDragGridToOneFifthVerb;
|
||||
SetDragGridToOff setDragGridToOffVerb;
|
||||
|
||||
SetGridSizeToTwo setGridSizeToTwoVerb;
|
||||
SetGridSizeToFour setGridSizeToFourVerb;
|
||||
SetGridSizeToSixteen setGridSizeToSixteenVerb;
|
||||
|
||||
SetManualJointToWeak setManualJointToWeakVerb;
|
||||
SetManualJointToStrong setManualJointToStrongVerb;
|
||||
SetManualJointToInfinite setManualJointToInfiniteVerb;
|
||||
|
||||
// Tools
|
||||
TToolVerb<AxisRotateTool> axisRotateToolVerb;
|
||||
TToolVerb<AdvMoveTool> advMoveToolVerb;
|
||||
TToolVerb<AdvRotateTool> advRotateToolVerb;
|
||||
TToolVerb<AdvArrowTool> advArrowToolVerb;
|
||||
TToolVerb<MoveResizeJoinTool> resizeToolVerb;
|
||||
|
||||
TToolVerb<FlatTool> flatToolVerb;
|
||||
TToolVerb<GlueTool> glueToolVerb;
|
||||
TToolVerb<WeldTool> weldToolVerb;
|
||||
TToolVerb<StudsTool> studsToolVerb;
|
||||
TToolVerb<InletTool> inletToolVerb;
|
||||
TToolVerb<UniversalTool> universalToolVerb;
|
||||
TToolVerb<HingeTool> hingeToolVerb;
|
||||
TToolVerb<RightMotorTool> rightMotorToolVerb;
|
||||
TToolVerb<LeftMotorTool> leftMotorToolVerb;
|
||||
TToolVerb<OscillateMotorTool> oscillateMotorToolVerb;
|
||||
TToolVerb<SmoothNoOutlinesTool> smoothNoOutlinesToolVerb;
|
||||
|
||||
TToolVerb<AnchorTool> anchorToolVerb;
|
||||
TToolVerb<LockTool> lockToolVerb;
|
||||
|
||||
TToolVerb<FillTool> fillToolVerb;
|
||||
TToolVerb<MaterialTool> materialToolVerb;
|
||||
TToolVerb<DropperTool> dropperToolVerb;
|
||||
|
||||
// Runtime Tools
|
||||
TToolVerb<GameTool> gameToolVerb;
|
||||
TToolVerb<GrabTool> grabToolVerb;
|
||||
TToolVerb<CloneTool> cloneToolVerb;
|
||||
TToolVerb<HammerTool> hammerToolVerb;
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
61
engine/app/src/DataModel/Configuration.cpp
Normal file
61
engine/app/src/DataModel/Configuration.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
|
||||
|
||||
#include "DataModel/Configuration.hpp"
|
||||
#include "DataModel/CollectionService.hpp"
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
#include "DataModel/ModelInstance.hpp"
|
||||
#include "DataModel/Value.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
const char* const sConfiguration = "Configuration";
|
||||
|
||||
Configuration::Configuration()
|
||||
: DescribedCreatable<Configuration, Instance, sConfiguration>()
|
||||
{
|
||||
setName("Configuration");
|
||||
}
|
||||
|
||||
|
||||
bool Configuration::askForbidChild(const Instance* instance) const
|
||||
{
|
||||
return (dynamic_cast<const IValue*>(instance) == NULL);
|
||||
}
|
||||
|
||||
bool Configuration::askSetParent(const Instance* instance) const
|
||||
{
|
||||
if (!fastDynamicCast<PartInstance>(instance))
|
||||
if (instance->getDescriptor() != ModelInstance::classDescriptor()) // ModelInstance, but not Workspace
|
||||
return false;
|
||||
|
||||
if (instance->getChildren())
|
||||
{
|
||||
Instances::const_iterator end = instance->getChildren()->end();
|
||||
for (Instances::const_iterator iter = instance->getChildren()->begin(); iter != end; ++iter)
|
||||
{
|
||||
if (fastDynamicCast<Configuration>(iter->get()))
|
||||
// There can be only one Configuration object per part
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Configuration::onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
|
||||
{
|
||||
if (oldProvider)
|
||||
{
|
||||
oldProvider->create<CollectionService>()->removeInstance(shared_from(this));
|
||||
}
|
||||
|
||||
Super::onServiceProvider(oldProvider, newProvider);
|
||||
|
||||
if (newProvider)
|
||||
{
|
||||
newProvider->create<CollectionService>()->addInstance(shared_from(this));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Aya
|
||||
26
engine/app/src/DataModel/Configuration.hpp
Normal file
26
engine/app/src/DataModel/Configuration.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Tree/Instance.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
extern const char* const sConfiguration;
|
||||
class Configuration : public DescribedCreatable<Configuration, Instance, sConfiguration>
|
||||
{
|
||||
private:
|
||||
typedef DescribedCreatable<Configuration, Instance, sConfiguration> Super;
|
||||
|
||||
public:
|
||||
Configuration();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Instance
|
||||
/*override*/ bool askForbidChild(const Instance* instance) const;
|
||||
/*override*/ bool askSetParent(const Instance* instance) const;
|
||||
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
|
||||
};
|
||||
} // namespace Aya
|
||||
1364
engine/app/src/DataModel/ContentProvider.cpp
Normal file
1364
engine/app/src/DataModel/ContentProvider.cpp
Normal file
File diff suppressed because it is too large
Load Diff
315
engine/app/src/DataModel/ContentProvider.hpp
Normal file
315
engine/app/src/DataModel/ContentProvider.hpp
Normal file
@@ -0,0 +1,315 @@
|
||||
#pragma once
|
||||
|
||||
#include "stdio.h"
|
||||
#include "Utility/Name.hpp"
|
||||
#include <string>
|
||||
#include <istream>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "boost.hpp"
|
||||
#include "time.hpp"
|
||||
#include "Utility/ContentId.hpp"
|
||||
#include "Utility/HeartbeatInstance.hpp"
|
||||
#include "Utility/ThreadPool.hpp"
|
||||
#include "Utility/AsyncHttpCache.hpp"
|
||||
#include "Utility/LRUCache.hpp"
|
||||
#include "Tree/Service.hpp"
|
||||
#include "Log.hpp"
|
||||
#include "Utility/ProtectedString.hpp"
|
||||
#include "boost/filesystem.hpp"
|
||||
#include "boost/optional.hpp"
|
||||
|
||||
|
||||
void SetAssetFolder(const std::string& folder);
|
||||
std::string GetAssetFolder();
|
||||
|
||||
void SetPlatformAssetFolder(const std::string& folder);
|
||||
std::string GetPlatformAssetFolder();
|
||||
|
||||
void SetItemsFolder(const std::string& folder);
|
||||
std::string GetItemsFolder();
|
||||
|
||||
void SetLevelsFolder(const std::string& folder);
|
||||
std::string GetLevelsFolder();
|
||||
|
||||
void SetModelsFolder(const std::string& folder);
|
||||
std::string GetModelsFolder();
|
||||
|
||||
void SetShadersFolder(const std::string& folder);
|
||||
std::string GetShadersFolder();
|
||||
|
||||
void SetPluginsFolder(const std::string& folder);
|
||||
std::string GetPluginsFolder();
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class AssetFetchMediator;
|
||||
class Instance;
|
||||
class Http;
|
||||
|
||||
extern const char* const sContentProvider;
|
||||
class ContentProvider
|
||||
: public DescribedNonCreatable<ContentProvider, Instance, sContentProvider, Reflection::ClassDescriptor::RUNTIME_LOCAL>
|
||||
, public Service
|
||||
, public HeartbeatInstance
|
||||
{
|
||||
private:
|
||||
typedef DescribedNonCreatable<ContentProvider, Instance, sContentProvider, Reflection::ClassDescriptor::RUNTIME_LOCAL> Super;
|
||||
|
||||
public:
|
||||
static Log* appLog;
|
||||
static Aya::mutex* appLogLock;
|
||||
|
||||
static float PRIORITY_DEFAULT;
|
||||
|
||||
static float PRIORITY_MFC;
|
||||
static float PRIORITY_SCRIPT;
|
||||
static float PRIORITY_MESH;
|
||||
static float PRIORITY_SOLIDMODEL;
|
||||
static float PRIORITY_INSERT;
|
||||
static float PRIORITY_CHARACTER;
|
||||
static float PRIORITY_ANIMATION;
|
||||
static float PRIORITY_TEXTURE;
|
||||
static float PRIORITY_DECAL;
|
||||
static float PRIORITY_SOUND;
|
||||
static float PRIORITY_GUI;
|
||||
static float PRIORITY_SKY;
|
||||
|
||||
private:
|
||||
boost::shared_ptr<boost::thread> legacyContentCleanupThread;
|
||||
boost::shared_ptr<boost::thread> contentCleanupThread;
|
||||
|
||||
static boost::filesystem::path assetFolderPath;
|
||||
static boost::filesystem::path platformAssetFolderPath;
|
||||
static boost::filesystem::path itemsFolderPath;
|
||||
static boost::filesystem::path levelsFolderPath;
|
||||
static boost::filesystem::path modelsFolderPath;
|
||||
static boost::filesystem::path shadersFolderPath;
|
||||
static boost::filesystem::path pluginsFolderPath;
|
||||
static std::string assetFolderString;
|
||||
static std::string platformAssetFolderString;
|
||||
static std::string itemsFolderString;
|
||||
static std::string levelsFolderString;
|
||||
static std::string modelsFolderString;
|
||||
static std::string shadersFolderString;
|
||||
static std::string pluginsFolderString;
|
||||
static bool assetFolderAlreadyInit;
|
||||
|
||||
struct CachedContent
|
||||
{
|
||||
shared_ptr<const std::string> data;
|
||||
shared_ptr<const std::string> filename;
|
||||
CachedContent() {}
|
||||
CachedContent(shared_ptr<const std::string> filename)
|
||||
: filename(filename)
|
||||
{
|
||||
}
|
||||
CachedContent(shared_ptr<const std::string> data, shared_ptr<const std::string> filename)
|
||||
: data(data)
|
||||
, filename(filename)
|
||||
{
|
||||
}
|
||||
};
|
||||
boost::shared_ptr<AsyncHttpCache<CachedContent>> contentCache;
|
||||
|
||||
class PreloadAsyncRequest
|
||||
{
|
||||
public:
|
||||
int outstanding;
|
||||
int failed;
|
||||
|
||||
PreloadAsyncRequest()
|
||||
: outstanding(0)
|
||||
, failed(0)
|
||||
{
|
||||
}
|
||||
|
||||
PreloadAsyncRequest(int requestCount)
|
||||
: outstanding(requestCount)
|
||||
, failed(0)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
ContentProvider();
|
||||
~ContentProvider();
|
||||
|
||||
static ContentId registerContent(std::istream& stream);
|
||||
|
||||
bool isUrlBad(Aya::ContentId id);
|
||||
|
||||
void blockingLoadInstances(ContentId id, std::vector<shared_ptr<Instance>>& instances);
|
||||
|
||||
bool isRequestQueueEmpty();
|
||||
|
||||
const std::string& getInstanceName() const;
|
||||
const std::string& getInstanceCurrency() const;
|
||||
const std::string& getInstanceMotd() const;
|
||||
|
||||
const std::string& getBaseUrl() const;
|
||||
const std::string getApiBaseUrl() const;
|
||||
const std::string getUnsecureApiBaseUrl() const;
|
||||
static std::string getApiBaseUrl(const std::string& baseUrl);
|
||||
static std::string getUnsecureApiBaseUrl(const std::string& baseUrl);
|
||||
|
||||
void setInstanceName(std::string name);
|
||||
void setInstanceCurrency(std::string currency);
|
||||
void setInstanceMotd(std::string motd);
|
||||
void setBaseUrl(std::string url);
|
||||
void setThreadPool(int count);
|
||||
void setCacheSize(int count);
|
||||
void preloadContentWithCallback(Aya::ContentId id, float priority, boost::function<void(AsyncHttpQueue::RequestResult)> callback,
|
||||
AsyncHttpQueue::ResultJob jobType = AsyncHttpQueue::AsyncInline, const std::string& expectedType = "");
|
||||
void preloadContent(Aya::ContentId id);
|
||||
|
||||
void preloadContentBlockingList(
|
||||
shared_ptr<const Reflection::ValueArray> idList, boost::function<void()> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
static boost::mutex preloadContentBlockingMutex;
|
||||
void preloadContentBlockingListHelper(AsyncHttpQueue::RequestResult results, boost::function<void()> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction, PreloadAsyncRequest* pRequest, ContentId id);
|
||||
void preloadContentResultCallback(AsyncHttpQueue::RequestResult results, ContentId id);
|
||||
|
||||
void clearContent();
|
||||
void invalidateCache(ContentId contentId);
|
||||
|
||||
// returns true if the content is available.
|
||||
bool hasContent(const ContentId& id);
|
||||
|
||||
// returns non-NULL if the content is available. If not available, the provider does an asynchronous request of the content for later
|
||||
shared_ptr<const std::string> requestContentString(const ContentId& id, float priority);
|
||||
|
||||
// Async request
|
||||
void getContent(const Aya::ContentId& id, float priority, AsyncHttpQueue::RequestCallback callback,
|
||||
AsyncHttpQueue::ResultJob jobType = AsyncHttpQueue::AsyncInline, const std::string& expectedType = "");
|
||||
void loadContent(const Aya::ContentId& id, float priority,
|
||||
boost::function<void(AsyncHttpQueue::RequestResult, shared_ptr<Instances>, shared_ptr<std::exception>)> callback,
|
||||
AsyncHttpQueue::ResultJob jobType = AsyncHttpQueue::AsyncInline);
|
||||
void loadContentString(const Aya::ContentId& id, float priority,
|
||||
boost::function<void(AsyncHttpQueue::RequestResult, shared_ptr<const std::string>, shared_ptr<std::exception>)> callback,
|
||||
AsyncHttpQueue::ResultJob jobType = AsyncHttpQueue::AsyncInline);
|
||||
|
||||
// The following functions throw exceptions upon failure or return NULL
|
||||
shared_ptr<const std::string> getContentString(ContentId id);
|
||||
std::auto_ptr<std::istream> getContent(const ContentId& contentId, const std::string& expectedType = "");
|
||||
std::string getFile(ContentId contentId);
|
||||
static std::string getAssetFile(const char* filePath);
|
||||
|
||||
// throws an exception
|
||||
static void verifyRequestedScriptSignature(const ProtectedString& source, const std::string& assetId, bool required);
|
||||
static void verifyScriptSignature(const ProtectedString& source, bool required);
|
||||
|
||||
int getRequestQueueSize() const;
|
||||
shared_ptr<const Reflection::ValueArray> getFailedUrls();
|
||||
shared_ptr<const Reflection::ValueArray> getRequestQueueUrls();
|
||||
shared_ptr<const Reflection::ValueArray> getRequestedUrls();
|
||||
|
||||
static void setAssetFolder(const char* path);
|
||||
static void setPlatformAssetFolder(const char* path);
|
||||
static void setLevelsFolder(const char* path);
|
||||
static void setItemsFolder(const char* path);
|
||||
static void setModelsFolder(const char* path);
|
||||
static void setShadersFolder(const char* path);
|
||||
static void setPluginsFolder(const char* path);
|
||||
static std::string getAssetFolder()
|
||||
{
|
||||
return assetFolderPath.string();
|
||||
};
|
||||
static std::string assetFolder();
|
||||
static std::string platformAssetFolder();
|
||||
static std::string getPlatformAssetFolder()
|
||||
{
|
||||
return platformAssetFolderPath.string();
|
||||
};
|
||||
static std::string itemsFolder();
|
||||
static std::string getItemsFolder()
|
||||
{
|
||||
return itemsFolderPath.string();
|
||||
};
|
||||
static std::string levelsFolder();
|
||||
static std::string getLevelsFolder()
|
||||
{
|
||||
return levelsFolderPath.string();
|
||||
};
|
||||
static std::string modelsFolder();
|
||||
static std::string getModelsFolder()
|
||||
{
|
||||
return modelsFolderPath.string();
|
||||
};
|
||||
static std::string shadersFolder();
|
||||
static std::string getShadersFolder()
|
||||
{
|
||||
return shadersFolderPath.string();
|
||||
};
|
||||
static std::string pluginsFolder();
|
||||
static std::string getPluginsFolder()
|
||||
{
|
||||
return pluginsFolderPath.string();
|
||||
};
|
||||
|
||||
static bool isUrl(const std::string& s);
|
||||
static bool isHttpUrl(const std::string& s);
|
||||
|
||||
static std::string findAsset(Aya::ContentId contentId);
|
||||
|
||||
static Reflection::PropDescriptor<ContentProvider, std::string> desc_baseUrl;
|
||||
static Reflection::PropDescriptor<ContentProvider, std::string> desc_instanceName;
|
||||
static Reflection::PropDescriptor<ContentProvider, std::string> desc_instanceCurrency;
|
||||
static Reflection::PropDescriptor<ContentProvider, std::string> desc_instanceMotd;
|
||||
|
||||
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
|
||||
{
|
||||
contentCache->resetStatsItem(newProvider);
|
||||
Super::onServiceProvider(oldProvider, newProvider);
|
||||
onServiceProviderHeartbeatInstance(oldProvider, newProvider); // hooks up heartbeat
|
||||
}
|
||||
/*implement*/ virtual void onHeartbeat(const Heartbeat& event);
|
||||
|
||||
void printContentNames()
|
||||
{
|
||||
contentCache->printContentNames();
|
||||
}
|
||||
|
||||
void setAssetFetchMediator(AssetFetchMediator* afm);
|
||||
|
||||
|
||||
private:
|
||||
// FullAsyncRequest: all requests (including disk requests) can be asynchronous. disk requests are returned as memory streams, exactly like http
|
||||
// responses.
|
||||
typedef enum
|
||||
{
|
||||
NoHttpRequest,
|
||||
AsyncHttpRequest,
|
||||
SyncHttpRequest,
|
||||
FullAsyncRequest
|
||||
} RequestType;
|
||||
|
||||
bool clearFinishFlag;
|
||||
AssetFetchMediator* afm;
|
||||
|
||||
std::string baseUrl;
|
||||
std::string instanceName;
|
||||
std::string instanceCurrency;
|
||||
std::string instanceMotd;
|
||||
|
||||
bool isContentLoaded(ContentId id);
|
||||
bool blockingLoadContent(ContentId id, CachedContent* result, const std::string& expectedType = "");
|
||||
AsyncHttpQueue::RequestResult privateLoadContent(ContentId& id, RequestType httpRequestType, float priority, CachedContent* result,
|
||||
AsyncHttpQueue::RequestCallback* callback, AsyncHttpQueue::ResultJob jobType = AsyncHttpQueue::AsyncInline,
|
||||
const std::string& expectedType = "");
|
||||
|
||||
static std::string findHashFile(ContentId contentId);
|
||||
static bool findLocalFile(const std::string& url, std::string* filename);
|
||||
|
||||
bool registerFile(const ContentId& id, CachedContent* item);
|
||||
|
||||
static bool isInSandbox(const boost::filesystem::path& path, const boost::filesystem::path& sandbox);
|
||||
};
|
||||
|
||||
class AssetFetchMediator
|
||||
{
|
||||
public:
|
||||
virtual boost::optional<std::string> findCachedAssetOrEmpty(const ContentId& contentId, int universeId) = 0;
|
||||
};
|
||||
} // namespace Aya
|
||||
948
engine/app/src/DataModel/ContextActionService.cpp
Normal file
948
engine/app/src/DataModel/ContextActionService.cpp
Normal file
@@ -0,0 +1,948 @@
|
||||
|
||||
//
|
||||
// ContextActionService.cpp
|
||||
// App
|
||||
//
|
||||
// Created by Ben Tkacheff on 1/17/13.
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
#include "DataModel/ContextActionService.hpp"
|
||||
|
||||
#include "DataModel/ModelInstance.hpp"
|
||||
#include "DataModel/GuiService.hpp"
|
||||
#include "DataModel/Tool.hpp"
|
||||
#include "DataModel/UserInputService.hpp"
|
||||
#include "DataModel/GuiObject.hpp"
|
||||
#include "Script/ScriptContext.hpp"
|
||||
|
||||
#include "Players.hpp"
|
||||
|
||||
DYNAMIC_FASTFLAGVARIABLE(TurnOffFakeEventsForCAS, false)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
const char* const sContextActionService = "ContextActionService";
|
||||
|
||||
|
||||
// tool convenience functions (todo: remove these)
|
||||
static Reflection::BoundFuncDesc<ContextActionService, std::string()> func_getCurrentLocalToolIcon(
|
||||
&ContextActionService::getCurrentLocalToolIcon, "GetCurrentLocalToolIcon", Security::None);
|
||||
|
||||
static Reflection::EventDesc<ContextActionService, void(shared_ptr<Instance>)> event_LocalToolEquipped(
|
||||
&ContextActionService::equippedToolSignal, "LocalToolEquipped", "toolEquipped");
|
||||
static Reflection::EventDesc<ContextActionService, void(shared_ptr<Instance>)> event_LocalToolUnequipped(
|
||||
&ContextActionService::unequippedToolSignal, "LocalToolUnequipped", "toolUnequipped");
|
||||
|
||||
|
||||
|
||||
// lua function to action binding (user facing functions)
|
||||
static Reflection::BoundFuncDesc<ContextActionService, void(std::string, Lua::WeakFunctionRef, bool, shared_ptr<const Reflection::Tuple>)>
|
||||
func_bindCore(&ContextActionService::bindCoreActionForInputTypes, "BindCoreAction", "actionName", "functionToBind", "createTouchButton",
|
||||
"inputTypes", Security::RobloxScript);
|
||||
static Reflection::BoundFuncDesc<ContextActionService, void(std::string, Lua::WeakFunctionRef, bool, shared_ptr<const Reflection::Tuple>)> func_bind(
|
||||
&ContextActionService::bindActionForInputTypes, "BindAction", "actionName", "functionToBind", "createTouchButton", "inputTypes", Security::None);
|
||||
static Reflection::BoundFuncDesc<ContextActionService, void(std::string, Lua::WeakFunctionRef, bool, shared_ptr<const Reflection::Tuple>)>
|
||||
func_bind_old(&ContextActionService::bindActionForInputTypes, "BindActionToInputTypes", "actionName", "functionToBind", "createTouchButton",
|
||||
"inputTypes", Security::None, Reflection::Descriptor::Attributes::deprecated(func_bind));
|
||||
|
||||
static Reflection::BoundFuncDesc<ContextActionService, void(InputObject::UserInputType, KeyCode)> func_bindActivate(
|
||||
&ContextActionService::bindActivate, "BindActivate", "userInputTypeForActivation", "keyCodeForActivation", SDLK_UNKNOWN, Security::None);
|
||||
static Reflection::BoundFuncDesc<ContextActionService, void(InputObject::UserInputType, KeyCode)> func_unbindActivate(
|
||||
&ContextActionService::unbindActivate, "UnbindActivate", "userInputTypeForActivation", "keyCodeForActivation", SDLK_UNKNOWN, Security::None);
|
||||
|
||||
// touch button interface
|
||||
static Reflection::BoundFuncDesc<ContextActionService, void(std::string, std::string)> func_AddTitle(
|
||||
&ContextActionService::setTitleForAction, "SetTitle", "actionName", "title", Security::None);
|
||||
static Reflection::BoundFuncDesc<ContextActionService, void(std::string, std::string)> func_AddDesc(
|
||||
&ContextActionService::setDescForAction, "SetDescription", "actionName", "description", Security::None);
|
||||
static Reflection::BoundFuncDesc<ContextActionService, void(std::string, std::string)> func_AddImage(
|
||||
&ContextActionService::setImageForAction, "SetImage", "actionName", "image", Security::None);
|
||||
static Reflection::BoundFuncDesc<ContextActionService, void(std::string, UDim2)> func_SetPosition(
|
||||
&ContextActionService::setPositionForAction, "SetPosition", "actionName", "position", Security::None);
|
||||
static Reflection::BoundYieldFuncDesc<ContextActionService, shared_ptr<Instance>(std::string)> func_GetButton(
|
||||
&ContextActionService::getButton, "GetButton", "actionName", Security::None);
|
||||
|
||||
// unbinding
|
||||
static Reflection::BoundFuncDesc<ContextActionService, void(std::string)> func_unbindCoreAction(
|
||||
&ContextActionService::unbindCoreAction, "UnbindCoreAction", "actionName", Security::RobloxScript);
|
||||
static Reflection::BoundFuncDesc<ContextActionService, void(std::string)> func_unbindAction(
|
||||
&ContextActionService::unbindAction, "UnbindAction", "actionName", Security::None);
|
||||
static Reflection::BoundFuncDesc<ContextActionService, void()> func_unbindAll(&ContextActionService::unbindAll, "UnbindAllActions", Security::None);
|
||||
|
||||
// bound info get
|
||||
static Reflection::BoundFuncDesc<ContextActionService, shared_ptr<const Reflection::ValueTable>(std::string)> func_getBoundFunctionData(
|
||||
&ContextActionService::getBoundActionData, "GetBoundActionInfo", "actionName", Security::None);
|
||||
static Reflection::BoundFuncDesc<ContextActionService, shared_ptr<const Reflection::ValueTable>()> func_getAllBoundFunctionData(
|
||||
&ContextActionService::getAllBoundActionData, "GetAllBoundActionInfo", Security::None);
|
||||
|
||||
// backend signals (for core scripts, not user exposed)
|
||||
static Reflection::EventDesc<ContextActionService, void(std::string, std::string, shared_ptr<const Reflection::ValueTable>)>
|
||||
event_boundFunctionUpdated(
|
||||
&ContextActionService::boundActionChangedSignal, "BoundActionChanged", "actionChanged", "changeName", "changeTable", Security::RobloxScript);
|
||||
static Reflection::EventDesc<ContextActionService, void(std::string, bool, shared_ptr<const Reflection::ValueTable>)> event_addedBoundFunction(
|
||||
&ContextActionService::boundActionAddedSignal, "BoundActionAdded", "actionAdded", "createTouchButton", "functionInfoTable",
|
||||
Security::RobloxScript);
|
||||
static Reflection::EventDesc<ContextActionService, void(std::string, shared_ptr<const Reflection::ValueTable>)> event_removedBoundFunction(
|
||||
&ContextActionService::boundActionRemovedSignal, "BoundActionRemoved", "actionRemoved", "functionInfoTable", Security::RobloxScript);
|
||||
|
||||
static Reflection::EventDesc<ContextActionService, void(std::string)> event_getActionButton(
|
||||
&ContextActionService::getActionButtonSignal, "GetActionButtonEvent", "actionName", Security::RobloxScript);
|
||||
static Reflection::BoundFuncDesc<ContextActionService, void(std::string, shared_ptr<Instance>)> func_actionButtonFound(
|
||||
&ContextActionService::fireActionButtonFoundSignal, "FireActionButtonFoundSignal", "actionName", "actionButton", Security::RobloxScript);
|
||||
|
||||
static Reflection::BoundFuncDesc<ContextActionService, void(std::string, InputObject::UserInputState, shared_ptr<Instance>)> func_callFunction(
|
||||
&ContextActionService::callFunction, "CallFunction", "actionName", "state", "inputObject", Security::RobloxScript);
|
||||
REFLECTION_END();
|
||||
|
||||
ContextActionService::ContextActionService()
|
||||
: Super()
|
||||
{
|
||||
setName(sContextActionService);
|
||||
|
||||
Aya::Guid::generateRBXGUID(activateGuid);
|
||||
}
|
||||
|
||||
Tool* ContextActionService::getCurrentLocalTool()
|
||||
{
|
||||
Aya::Network::Players* players = ServiceProvider::create<Aya::Network::Players>(this);
|
||||
if (Aya::Network::Player* player = players->getLocalPlayer())
|
||||
if (ModelInstance* character = player->getCharacter())
|
||||
if (Aya::Tool* activeTool = character->findFirstChildOfType<Aya::Tool>())
|
||||
return activeTool;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::string ContextActionService::getCurrentLocalToolIcon()
|
||||
{
|
||||
if (Tool* activeTool = getCurrentLocalTool())
|
||||
return activeTool->getTextureId().c_str();
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void ContextActionService::disconnectAllCharacterConnections()
|
||||
{
|
||||
characterChildAddConnection.disconnect();
|
||||
characterChildRemoveConnection.disconnect();
|
||||
}
|
||||
|
||||
Tool* ContextActionService::isTool(shared_ptr<Instance> instance)
|
||||
{
|
||||
if (Instance* baldInstance = instance.get())
|
||||
if (Tool* tool = Instance::fastDynamicCast<Tool>(baldInstance))
|
||||
return tool;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ContextActionService::checkForNewTool(shared_ptr<Instance> newChildOfCharacter)
|
||||
{
|
||||
if (Tool* newTool = isTool(newChildOfCharacter))
|
||||
equippedToolSignal(shared_from(newTool));
|
||||
}
|
||||
|
||||
void ContextActionService::checkForToolRemoval(shared_ptr<Instance> removedChildOfCharacter)
|
||||
{
|
||||
if (Tool* removedTool = isTool(removedChildOfCharacter))
|
||||
unequippedToolSignal(shared_from(removedTool));
|
||||
}
|
||||
|
||||
void ContextActionService::setupLocalCharacterConnections(ModelInstance* character)
|
||||
{
|
||||
characterChildAddConnection = character->onDemandWrite()->childAddedSignal.connect(boost::bind(&ContextActionService::checkForNewTool, this, _1));
|
||||
characterChildRemoveConnection =
|
||||
character->onDemandWrite()->childRemovedSignal.connect(boost::bind(&ContextActionService::checkForToolRemoval, this, _1));
|
||||
}
|
||||
|
||||
void ContextActionService::localCharacterAdded(shared_ptr<Instance> character)
|
||||
{
|
||||
disconnectAllCharacterConnections();
|
||||
if (ModelInstance* characterModel = Instance::fastDynamicCast<ModelInstance>(character.get()))
|
||||
setupLocalCharacterConnections(characterModel);
|
||||
}
|
||||
|
||||
void ContextActionService::checkForLocalPlayer(shared_ptr<Instance> newPlayer)
|
||||
{
|
||||
if (Aya::Network::Players* players = ServiceProvider::create<Aya::Network::Players>(this))
|
||||
{
|
||||
if (Aya::Network::Player* player = Instance::fastDynamicCast<Aya::Network::Player>(newPlayer.get()))
|
||||
{
|
||||
if (player == players->getLocalPlayer())
|
||||
{
|
||||
localPlayerAddConnection.disconnect();
|
||||
setupLocalPlayerConnections(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ContextActionService::setupLocalPlayerConnections(Aya::Network::Player* localPlayer)
|
||||
{
|
||||
if (ModelInstance* character = localPlayer->getCharacter())
|
||||
localCharacterAdded(shared_from(character));
|
||||
else
|
||||
localPlayer->characterAddedSignal.connect(boost::bind(&ContextActionService::localCharacterAdded, this, _1));
|
||||
}
|
||||
|
||||
void ContextActionService::onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
|
||||
{
|
||||
if (oldProvider)
|
||||
{
|
||||
// currently nothing needed to be done here (we disconnect on new adds)
|
||||
}
|
||||
|
||||
Super::onServiceProvider(oldProvider, newProvider);
|
||||
|
||||
if (newProvider)
|
||||
{
|
||||
Aya::Network::Players* players = ServiceProvider::create<Aya::Network::Players>(newProvider);
|
||||
if (Aya::Network::Player* player = players->getLocalPlayer())
|
||||
setupLocalPlayerConnections(player);
|
||||
else
|
||||
localPlayerAddConnection =
|
||||
players->onDemandWrite()->childAddedSignal.connect(boost::bind(&ContextActionService::checkForLocalPlayer, this, _1));
|
||||
}
|
||||
}
|
||||
|
||||
bool processInputForPlayerMovement(const PlayerActionType& playerMovement, const shared_ptr<InputObject>& inputObject)
|
||||
{
|
||||
// todo: remove hard coded keys, allow users to set this
|
||||
if (inputObject->isKeyEvent())
|
||||
{
|
||||
switch (inputObject->getKeyCode())
|
||||
{
|
||||
case SDLK_w:
|
||||
{
|
||||
if (playerMovement == CHARACTER_FORWARD)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDLK_UP:
|
||||
{
|
||||
if (playerMovement == CHARACTER_FORWARD)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDLK_s:
|
||||
{
|
||||
if (playerMovement == CHARACTER_BACKWARD)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDLK_DOWN:
|
||||
{
|
||||
if (playerMovement == CHARACTER_BACKWARD)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDLK_a:
|
||||
{
|
||||
if (playerMovement == CHARACTER_LEFT)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDLK_d:
|
||||
{
|
||||
if (playerMovement == CHARACTER_RIGHT)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDLK_SPACE:
|
||||
{
|
||||
if (playerMovement == CHARACTER_JUMP)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tupleContainsInputObject(shared_ptr<const Reflection::Tuple> inputBindings, const shared_ptr<InputObject>& inputObject, bool& sinkInput)
|
||||
{
|
||||
sinkInput = true;
|
||||
if (inputBindings && inputBindings->values.size() > 0)
|
||||
{
|
||||
bool inputObjectIsKeyEvent = inputObject->isKeyEvent() || inputObject->isGamepadEvent();
|
||||
Reflection::ValueArray inputBindingsValues = inputBindings->values;
|
||||
|
||||
// loop thru all the input bindings and compare them with the input object
|
||||
for (Reflection::ValueArray::iterator tupleIter = inputBindingsValues.begin(); tupleIter != inputBindingsValues.end(); ++tupleIter)
|
||||
{
|
||||
// we have a key event, check and see if user passed in key code, or string that is key code
|
||||
if (inputObjectIsKeyEvent)
|
||||
{
|
||||
KeyCode keyCode = SDLK_UNKNOWN;
|
||||
|
||||
if (tupleIter->isType<KeyCode>())
|
||||
{
|
||||
keyCode = tupleIter->cast<KeyCode>();
|
||||
}
|
||||
else if (tupleIter->isString())
|
||||
{
|
||||
std::string keyString = tupleIter->get<std::string>();
|
||||
if (keyString.length() == 1)
|
||||
{
|
||||
keyCode = (KeyCode)keyString[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (keyCode != SDLK_UNKNOWN && inputObject->getKeyCode() == keyCode)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tupleIter->isType<InputObject::UserInputType>())
|
||||
{
|
||||
InputObject::UserInputType inputType = tupleIter->cast<InputObject::UserInputType>();
|
||||
if (inputType == inputObject->getUserInputType())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (tupleIter->isType<PlayerActionType>())
|
||||
{
|
||||
PlayerActionType playerMovement = tupleIter->cast<PlayerActionType>();
|
||||
if (processInputForPlayerMovement(playerMovement, inputObject))
|
||||
{
|
||||
sinkInput = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// otherwise we might have a userinputtype, check against these
|
||||
else if (tupleIter->isType<InputObject::UserInputType>())
|
||||
{
|
||||
InputObject::UserInputType inputType = tupleIter->cast<InputObject::UserInputType>();
|
||||
if (inputType == inputObject->getUserInputType())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sinkInput = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
void ContextActionService::callFunction(boost::function<void(shared_ptr<Reflection::Tuple>)> luaFunction, const std::string actionName,
|
||||
const InputObject::UserInputState state, const shared_ptr<Instance> inputObject)
|
||||
{
|
||||
if (!luaFunction.empty())
|
||||
{
|
||||
shared_ptr<Reflection::Tuple> args = Aya::make_shared<Reflection::Tuple>();
|
||||
args->values.push_back(actionName);
|
||||
args->values.push_back(state);
|
||||
args->values.push_back(inputObject);
|
||||
|
||||
luaFunction(args);
|
||||
}
|
||||
}
|
||||
|
||||
void ContextActionService::callFunction(const std::string actionName, const InputObject::UserInputState state, const shared_ptr<Instance> inputObject)
|
||||
{
|
||||
FunctionMap::iterator iter = findAction(actionName);
|
||||
if (iter == functionMap.end())
|
||||
{
|
||||
StandardOut::singleton()->printf(MESSAGE_ERROR, "ContextActionService::CallFunction does have a function for %s", actionName.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
callFunction(iter->second.luaFunction, actionName, state, inputObject);
|
||||
}
|
||||
|
||||
GuiResponse ContextActionService::tryProcess(shared_ptr<InputObject> inputObject, FunctionVector& funcVector, bool menuIsOpen)
|
||||
{
|
||||
if (DFFlag::TurnOffFakeEventsForCAS && !inputObject->isPublicEvent())
|
||||
return GuiResponse::notSunk();
|
||||
if (!inputObject)
|
||||
return GuiResponse::notSunk();
|
||||
if (!inputObject.get())
|
||||
return GuiResponse::notSunk();
|
||||
|
||||
for (FunctionVector::reverse_iterator iter = funcVector.rbegin(); iter != funcVector.rend(); ++iter)
|
||||
{
|
||||
if (iter->second.inputTypes && iter->second.inputTypes->values.size() > 0)
|
||||
{
|
||||
bool sink = false;
|
||||
if (tupleContainsInputObject(iter->second.inputTypes, inputObject, sink))
|
||||
{
|
||||
// first check to see if BindAction should call an action
|
||||
if (!iter->second.luaFunction.empty())
|
||||
{
|
||||
if (shared_ptr<Instance> instance = shared_from(Instance::fastDynamicCast<Instance>(inputObject.get())))
|
||||
{
|
||||
iter->second.lastInput = weak_from(inputObject.get());
|
||||
callFunction(iter->second.luaFunction, iter->first,
|
||||
menuIsOpen ? InputObject::INPUT_STATE_CANCEL : inputObject->getUserInputState(), instance);
|
||||
}
|
||||
|
||||
if (sink)
|
||||
return GuiResponse::sunk();
|
||||
|
||||
return GuiResponse::notSunk();
|
||||
}
|
||||
// see if we should fire Activate from BindActivate
|
||||
else if (iter->first.compare(activateGuid) == 0)
|
||||
{
|
||||
bool canActivate = false;
|
||||
Reflection::ValueArray activateTuple = iter->second.inputTypes->values;
|
||||
if (!activateTuple.empty() && activateTuple.begin()->isType<InputObject::UserInputType>() &&
|
||||
activateTuple.begin()->get<InputObject::UserInputType>() == inputObject->getUserInputType())
|
||||
{
|
||||
if (activateTuple.size() == 1)
|
||||
{
|
||||
canActivate = true;
|
||||
}
|
||||
else if (activateTuple.back().isType<KeyCode>() && activateTuple.back().get<KeyCode>() == inputObject->getKeyCode())
|
||||
{
|
||||
canActivate = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (canActivate && !menuIsOpen)
|
||||
{
|
||||
if (UserInputService* inputService = Aya::ServiceProvider::find<UserInputService>(this))
|
||||
{
|
||||
Vector2 currentPos = inputService->getCurrentMousePosition()->get2DPosition();
|
||||
bool isDown = inputObject->getUserInputState() == InputObject::INPUT_STATE_BEGIN;
|
||||
shared_ptr<InputObject> simulateInputObject = Aya::Creatable<Aya::Instance>::create<Aya::InputObject>(
|
||||
InputObject::TYPE_MOUSEBUTTON1, isDown ? InputObject::INPUT_STATE_BEGIN : InputObject::INPUT_STATE_END,
|
||||
Vector3(currentPos.x, currentPos.y, 0), Vector3::zero(), Aya::DataModel::get(this));
|
||||
|
||||
// r2 and l2 are special kinds of input, they don't have simple up and down states
|
||||
if (inputObject->getKeyCode() == SDLK_GAMEPAD_BUTTONR2 || inputObject->getKeyCode() == SDLK_GAMEPAD_BUTTONL2)
|
||||
{
|
||||
float zPos = inputObject->getPosition().z;
|
||||
float lastZPos = 0.0f;
|
||||
InputObject* input = inputObject.get();
|
||||
|
||||
if (lastZPositionsForActivate.find(input) != lastZPositionsForActivate.end())
|
||||
{
|
||||
lastZPos = lastZPositionsForActivate[input];
|
||||
}
|
||||
|
||||
if (zPos >= 0.5 && lastZPos < 0.5)
|
||||
{
|
||||
simulateInputObject->setInputState(InputObject::INPUT_STATE_BEGIN);
|
||||
Aya::DataModel::get(this)->processInputObject(simulateInputObject);
|
||||
}
|
||||
else if (zPos <= 0.2 && lastZPos > 0.2)
|
||||
{
|
||||
simulateInputObject->setInputState(InputObject::INPUT_STATE_END);
|
||||
Aya::DataModel::get(this)->processInputObject(simulateInputObject);
|
||||
}
|
||||
|
||||
lastZPositionsForActivate[input] = zPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
Aya::DataModel::get(this)->processInputObject(simulateInputObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// guess we have nothing, error out
|
||||
else
|
||||
{
|
||||
StandardOut::singleton()->printf(
|
||||
MESSAGE_ERROR, "ContextActionService::CallFunction does have a function for %s", iter->first.c_str());
|
||||
return GuiResponse::notSunk();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GuiResponse::notSunk();
|
||||
}
|
||||
|
||||
GuiResponse ContextActionService::processCoreBindings(const shared_ptr<InputObject>& inputObject)
|
||||
{
|
||||
return tryProcess(inputObject, coreFunctionVector, false);
|
||||
}
|
||||
|
||||
GuiResponse ContextActionService::processDevBindings(const shared_ptr<InputObject>& inputObject, bool menuIsOpen)
|
||||
{
|
||||
return tryProcess(inputObject, functionVector, menuIsOpen);
|
||||
}
|
||||
|
||||
static shared_ptr<Reflection::ValueTable> constructTableFromBoundFunctionData(const BoundFunctionData& functionData)
|
||||
{
|
||||
shared_ptr<Reflection::ValueTable> functionTable = Aya::make_shared<Reflection::ValueTable>();
|
||||
if (!functionData.title.empty())
|
||||
{
|
||||
(*functionTable)["title"] = functionData.title;
|
||||
}
|
||||
if (!functionData.image.empty())
|
||||
{
|
||||
(*functionTable)["image"] = functionData.image;
|
||||
}
|
||||
if (!functionData.description.empty())
|
||||
{
|
||||
(*functionTable)["description"] = functionData.description;
|
||||
}
|
||||
if (functionData.hasTouchButton)
|
||||
{
|
||||
(*functionTable)["createTouchButton"] = functionData.hasTouchButton;
|
||||
}
|
||||
if (functionData.inputTypes)
|
||||
{
|
||||
shared_ptr<Reflection::ValueArray> inputArray = Aya::make_shared<Reflection::ValueArray>(functionData.inputTypes->values);
|
||||
(*functionTable)["inputTypes"] = Reflection::Variant(shared_ptr<const Reflection::ValueArray>(inputArray));
|
||||
}
|
||||
|
||||
return functionTable;
|
||||
}
|
||||
|
||||
void unbindActionInternal(const std::string actionName, FunctionMap& funcMap, FunctionVector& funcVector,
|
||||
Aya::signal<void(std::string, shared_ptr<const Reflection::ValueTable>)>& removedSignal)
|
||||
{
|
||||
if (funcMap.find(actionName) != funcMap.end())
|
||||
{
|
||||
BoundFunctionData functionData = funcMap[actionName];
|
||||
|
||||
shared_ptr<const Reflection::ValueTable> functionTable = constructTableFromBoundFunctionData(functionData);
|
||||
|
||||
funcMap.erase(actionName);
|
||||
|
||||
for (FunctionVector::iterator iter = funcVector.begin(); iter != funcVector.end(); ++iter)
|
||||
{
|
||||
if ((*iter).first == actionName)
|
||||
{
|
||||
funcVector.erase(iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// now fire a signal so we know a new function was removed (used at least by core scripts)
|
||||
removedSignal(actionName, functionTable);
|
||||
}
|
||||
}
|
||||
|
||||
void ContextActionService::unbindCoreAction(const std::string actionName)
|
||||
{
|
||||
unbindActionInternal(actionName, coreFunctionMap, coreFunctionVector, boundActionRemovedSignal);
|
||||
}
|
||||
|
||||
void ContextActionService::unbindAction(const std::string actionName)
|
||||
{
|
||||
unbindActionInternal(actionName, functionMap, functionVector, boundActionRemovedSignal);
|
||||
}
|
||||
|
||||
void ContextActionService::unbindAll()
|
||||
{
|
||||
for (FunctionMap::iterator iter = functionMap.begin(); iter != functionMap.end(); ++iter)
|
||||
{
|
||||
shared_ptr<const Reflection::ValueTable> functionTable = constructTableFromBoundFunctionData(iter->second);
|
||||
|
||||
boundActionRemovedSignal((iter->first), functionTable);
|
||||
}
|
||||
|
||||
functionMap.clear();
|
||||
functionVector.clear();
|
||||
}
|
||||
static void InvokeCallback(weak_ptr<ContextActionService> weakActionService, Lua::WeakFunctionRef callback, shared_ptr<Reflection::Tuple> args)
|
||||
{
|
||||
if (shared_ptr<ContextActionService> strongThis = weakActionService.lock())
|
||||
{
|
||||
if (Lua::ThreadRef threadRef = callback.lock())
|
||||
{
|
||||
if (ScriptContext* sc = ServiceProvider::create<ScriptContext>(strongThis.get()))
|
||||
{
|
||||
try
|
||||
{
|
||||
sc->callInNewThread(callback, *(args.get()));
|
||||
}
|
||||
catch (Aya::base_exception& e)
|
||||
{
|
||||
StandardOut::singleton()->printf(MESSAGE_ERROR, "ContextActionService: Unexpected error while invoking callback: %s", e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ContextActionService::checkForInputOverride(const Reflection::Variant& newInputType, const FunctionVector& funcVector)
|
||||
{
|
||||
for (FunctionVector::const_reverse_iterator iter = funcVector.rbegin(); iter != funcVector.rend(); ++iter)
|
||||
{
|
||||
if (iter->second.inputTypes && iter->second.inputTypes->values.size() > 0)
|
||||
{
|
||||
Reflection::ValueArray inputBindingsValues = iter->second.inputTypes->values;
|
||||
|
||||
for (Reflection::ValueArray::const_iterator tupleIter = inputBindingsValues.begin(); tupleIter != inputBindingsValues.end(); ++tupleIter)
|
||||
{
|
||||
bool didOverride = false;
|
||||
|
||||
if (newInputType.isType<KeyCode>() && tupleIter->isType<KeyCode>())
|
||||
{
|
||||
didOverride = (tupleIter->cast<KeyCode>() == newInputType.cast<KeyCode>());
|
||||
}
|
||||
else if (newInputType.isString() && tupleIter->isString())
|
||||
{
|
||||
didOverride = (tupleIter->cast<std::string>() == newInputType.cast<std::string>());
|
||||
}
|
||||
else if (newInputType.isType<InputObject::UserInputType>() && tupleIter->isType<InputObject::UserInputType>())
|
||||
{
|
||||
didOverride = (tupleIter->cast<InputObject::UserInputType>() == newInputType.cast<InputObject::UserInputType>());
|
||||
}
|
||||
else if (newInputType.isType<PlayerActionType>() && tupleIter->isType<PlayerActionType>())
|
||||
{
|
||||
didOverride = (tupleIter->cast<PlayerActionType>() == newInputType.cast<PlayerActionType>());
|
||||
}
|
||||
|
||||
if (didOverride)
|
||||
{
|
||||
shared_ptr<InputObject> cancelInputObject = iter->second.lastInput.lock();
|
||||
InputObject::UserInputState prevInputState = InputObject::INPUT_STATE_NONE;
|
||||
|
||||
if (cancelInputObject)
|
||||
{
|
||||
prevInputState = cancelInputObject->getUserInputState();
|
||||
cancelInputObject->setInputState(InputObject::INPUT_STATE_CANCEL);
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelInputObject = Aya::Creatable<Aya::Instance>::create<Aya::InputObject>(Aya::InputObject::TYPE_NONE,
|
||||
Aya::InputObject::INPUT_STATE_CANCEL, Aya::Vector3::zero(), Aya::Vector3::zero(), Aya::DataModel::get(this));
|
||||
}
|
||||
|
||||
|
||||
if (shared_ptr<Instance> instance = shared_from(Instance::fastDynamicCast<Instance>(cancelInputObject.get())))
|
||||
{
|
||||
callFunction(iter->second.luaFunction, iter->first, InputObject::INPUT_STATE_CANCEL, instance);
|
||||
}
|
||||
|
||||
cancelInputObject->setInputState(prevInputState);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ContextActionService::bindActionInternal(const std::string actionName, Lua::WeakFunctionRef functionToBind, bool createTouchButton,
|
||||
shared_ptr<const Reflection::Tuple> hotkeys, FunctionMap& funcMap, FunctionVector& funcVector)
|
||||
{
|
||||
if (!Network::Players::frontendProcessing(this))
|
||||
{
|
||||
throw Aya::runtime_error("ContextActionService:BindAction can only be called from a local script");
|
||||
return;
|
||||
}
|
||||
|
||||
if (actionName.empty())
|
||||
{
|
||||
throw Aya::runtime_error("ContextActionService:BindAction called with an empty actionName");
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotkeys && !hotkeys->values.empty())
|
||||
{
|
||||
for (Reflection::ValueArray::const_iterator iter = hotkeys->values.begin(); iter != hotkeys->values.end(); ++iter)
|
||||
{
|
||||
if (!iter->isType<KeyCode>() && !iter->isString() && !iter->isType<InputObject::UserInputType>() && !iter->isType<PlayerActionType>())
|
||||
{
|
||||
throw Aya::runtime_error(
|
||||
"ContextActionService:BindAction called with an invalid hotkey (should be either Enum.KeyCode, Enum.UserInputType or string)");
|
||||
return;
|
||||
}
|
||||
|
||||
// Now check to see if this bind is overriding another bind action (so we can signal the action it is no longer active)
|
||||
|
||||
// core Aya overrides only happen when its another core bind
|
||||
if (funcVector == coreFunctionVector)
|
||||
{
|
||||
checkForInputOverride(*iter, coreFunctionVector);
|
||||
}
|
||||
// game developer overrides happen for all new binds
|
||||
checkForInputOverride(*iter, functionVector);
|
||||
}
|
||||
}
|
||||
|
||||
if (funcMap.find(actionName) != funcMap.end())
|
||||
{
|
||||
if (funcMap == coreFunctionMap)
|
||||
{
|
||||
unbindCoreAction(actionName);
|
||||
}
|
||||
else
|
||||
{
|
||||
unbindAction(actionName);
|
||||
}
|
||||
}
|
||||
|
||||
boost::function<void(shared_ptr<Reflection::Tuple>)> luaFunction = boost::bind(&InvokeCallback, shared_from(this), functionToBind, _1);
|
||||
|
||||
BoundFunctionData data;
|
||||
if (hotkeys)
|
||||
{
|
||||
data = BoundFunctionData(hotkeys, luaFunction, createTouchButton);
|
||||
}
|
||||
else
|
||||
{
|
||||
data = BoundFunctionData(luaFunction, createTouchButton);
|
||||
}
|
||||
|
||||
funcMap[actionName] = data;
|
||||
funcVector.push_back(std::pair<std::string, BoundFunctionData>(actionName, data));
|
||||
|
||||
// now fire a signal so we know a new function was bound (used by core scripts)
|
||||
shared_ptr<Reflection::ValueTable> functionTable(Aya::make_shared<Reflection::ValueTable>());
|
||||
|
||||
(*functionTable)["title"] = Aya::Reflection::Variant();
|
||||
(*functionTable)["image"] = Aya::Reflection::Variant();
|
||||
(*functionTable)["description"] = Aya::Reflection::Variant();
|
||||
(*functionTable)["createTouchButton"] = Aya::Reflection::Variant(createTouchButton);
|
||||
if (hotkeys && !hotkeys->values.empty())
|
||||
{
|
||||
shared_ptr<Reflection::ValueArray> hotkeyArray = Aya::make_shared<Reflection::ValueArray>(hotkeys->values);
|
||||
(*functionTable)["inputTypes"] = shared_ptr<const Aya::Reflection::ValueArray>(hotkeyArray);
|
||||
}
|
||||
|
||||
boundActionAddedSignal(actionName, createTouchButton, functionTable);
|
||||
}
|
||||
|
||||
void ContextActionService::bindCoreActionForInputTypes(
|
||||
const std::string actionName, Lua::WeakFunctionRef functionToBind, bool createTouchButton, shared_ptr<const Reflection::Tuple> hotkeys)
|
||||
{
|
||||
bindActionInternal(actionName, functionToBind, createTouchButton, hotkeys, coreFunctionMap, coreFunctionVector);
|
||||
}
|
||||
|
||||
|
||||
void ContextActionService::bindActionForInputTypes(
|
||||
const std::string actionName, Lua::WeakFunctionRef functionToBind, bool createTouchButton, shared_ptr<const Reflection::Tuple> hotkeys)
|
||||
{
|
||||
bindActionInternal(actionName, functionToBind, createTouchButton, hotkeys, functionMap, functionVector);
|
||||
}
|
||||
|
||||
void ContextActionService::bindActivate(InputObject::UserInputType inputType, KeyCode keyCode)
|
||||
{
|
||||
if (!Network::Players::frontendProcessing(this))
|
||||
{
|
||||
throw Aya::runtime_error("ContextActionService:BindActivate can only be called from a local script");
|
||||
}
|
||||
|
||||
if (inputType == InputObject::TYPE_NONE)
|
||||
{
|
||||
throw Aya::runtime_error("ContextActionService:BindActivate called with Enum.UserInputType.None, must be some other input type.");
|
||||
}
|
||||
|
||||
unbindActivate(inputType, keyCode);
|
||||
|
||||
shared_ptr<Reflection::Tuple> activateTuple = Aya::make_shared<Reflection::Tuple>();
|
||||
activateTuple->values.push_back(inputType);
|
||||
if (keyCode != SDLK_UNKNOWN)
|
||||
{
|
||||
activateTuple->values.push_back(keyCode);
|
||||
}
|
||||
|
||||
functionVector.push_back(std::pair<std::string, BoundFunctionData>(activateGuid, BoundFunctionData(activateTuple)));
|
||||
}
|
||||
|
||||
void ContextActionService::unbindActivate(InputObject::UserInputType inputType, KeyCode keyCode)
|
||||
{
|
||||
if (!Network::Players::frontendProcessing(this))
|
||||
{
|
||||
throw Aya::runtime_error("ContextActionService:UnbindActivate can only be called from a local script");
|
||||
}
|
||||
|
||||
if (inputType == InputObject::TYPE_NONE)
|
||||
{
|
||||
throw Aya::runtime_error("ContextActionService:UnbindActivate called with Enum.UserInputType.None, must be some other input type.");
|
||||
}
|
||||
|
||||
for (FunctionVector::reverse_iterator iter = functionVector.rbegin(); iter != functionVector.rend(); ++iter)
|
||||
{
|
||||
if ((*iter).first == activateGuid)
|
||||
{
|
||||
Reflection::ValueArray currentInputTypes = (*iter).second.inputTypes->values;
|
||||
if (!currentInputTypes.empty() && currentInputTypes.begin()->isType<InputObject::UserInputType>())
|
||||
{
|
||||
if (inputType == currentInputTypes.begin()->get<InputObject::UserInputType>())
|
||||
{
|
||||
functionVector.erase(--iter.base());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FunctionMap::iterator ContextActionService::findAction(const std::string actionName)
|
||||
{
|
||||
FunctionMap::iterator iter = functionMap.find(actionName);
|
||||
if (iter == functionMap.end())
|
||||
{
|
||||
StandardOut::singleton()->printf(MESSAGE_WARNING, "ContextActionService could not find the function passed in, doing nothing.");
|
||||
}
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
void ContextActionService::fireBoundActionChangedSignal(FunctionMap::iterator iter, const std::string& changeName)
|
||||
{
|
||||
if (iter == functionMap.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
shared_ptr<Reflection::ValueTable> functionTable = constructTableFromBoundFunctionData(iter->second);
|
||||
(*functionTable)["position"] = iter->second.position;
|
||||
|
||||
boundActionChangedSignal((iter->first), changeName, functionTable);
|
||||
}
|
||||
|
||||
void ContextActionService::setTitleForAction(const std::string actionName, std::string title)
|
||||
{
|
||||
FunctionMap::iterator iter = findAction(actionName);
|
||||
if (iter == functionMap.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
iter->second.title = title;
|
||||
fireBoundActionChangedSignal(iter, "title");
|
||||
}
|
||||
void ContextActionService::setDescForAction(const std::string actionName, std::string description)
|
||||
{
|
||||
FunctionMap::iterator iter = findAction(actionName);
|
||||
if (iter == functionMap.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
iter->second.description = description;
|
||||
fireBoundActionChangedSignal(iter, "description");
|
||||
}
|
||||
|
||||
void ContextActionService::setImageForAction(const std::string actionName, std::string image)
|
||||
{
|
||||
FunctionMap::iterator iter = findAction(actionName);
|
||||
if (iter == functionMap.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
iter->second.image = image;
|
||||
fireBoundActionChangedSignal(iter, "image");
|
||||
}
|
||||
|
||||
void ContextActionService::setPositionForAction(const std::string actionName, UDim2 position)
|
||||
{
|
||||
FunctionMap::iterator iter = findAction(actionName);
|
||||
if (iter == functionMap.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
iter->second.position = position;
|
||||
fireBoundActionChangedSignal(iter, "position");
|
||||
}
|
||||
|
||||
void ContextActionService::getButton(
|
||||
const std::string actionName, boost::function<void(shared_ptr<Instance>)> resumeFunction, boost::function<void(std::string)> errorFunction)
|
||||
{
|
||||
FunctionMap::iterator iter = findAction(actionName);
|
||||
if (iter == functionMap.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
yieldFunctionMap[actionName] = FunctionPair(resumeFunction, errorFunction);
|
||||
getActionButtonSignal(actionName);
|
||||
}
|
||||
|
||||
void ContextActionService::fireActionButtonFoundSignal(const std::string actionName, shared_ptr<Instance> actionButton)
|
||||
{
|
||||
try
|
||||
{
|
||||
yieldFunctionMap.at(actionName);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
StandardOut::singleton()->print(MESSAGE_ERROR, e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
FunctionPair functionPair = yieldFunctionMap[actionName];
|
||||
functionPair.first(actionButton);
|
||||
}
|
||||
|
||||
|
||||
shared_ptr<const Reflection::ValueTable> ContextActionService::getAllBoundActionData()
|
||||
{
|
||||
shared_ptr<Reflection::ValueTable> reflectedFuncMap = Aya::make_shared<Reflection::ValueTable>();
|
||||
|
||||
for (FunctionMap::iterator iter = functionMap.begin(); iter != functionMap.end(); ++iter)
|
||||
{
|
||||
if (iter != functionMap.end())
|
||||
{
|
||||
shared_ptr<const Reflection::ValueTable> functionInfo = getBoundActionData(iter->first);
|
||||
|
||||
Reflection::Variant variantValue = shared_ptr<const Aya::Reflection::ValueTable>(functionInfo);
|
||||
(*reflectedFuncMap)[iter->first] = variantValue;
|
||||
}
|
||||
}
|
||||
|
||||
return reflectedFuncMap;
|
||||
}
|
||||
|
||||
shared_ptr<const Reflection::ValueTable> ContextActionService::getBoundCoreActionData(const std::string actionName)
|
||||
{
|
||||
FunctionMap::iterator iter = coreFunctionMap.find(actionName);
|
||||
if (iter != coreFunctionMap.end())
|
||||
{
|
||||
return constructTableFromBoundFunctionData(iter->second);
|
||||
}
|
||||
|
||||
return Aya::make_shared<Reflection::ValueTable>();
|
||||
}
|
||||
|
||||
shared_ptr<const Reflection::ValueTable> ContextActionService::getBoundActionData(const std::string actionName)
|
||||
{
|
||||
FunctionMap::iterator iter = functionMap.find(actionName);
|
||||
if (iter != functionMap.end())
|
||||
{
|
||||
return constructTableFromBoundFunctionData(iter->second);
|
||||
}
|
||||
|
||||
return Aya::make_shared<Reflection::ValueTable>();
|
||||
}
|
||||
|
||||
namespace Reflection
|
||||
{
|
||||
template<>
|
||||
EnumDesc<PlayerActionType>::EnumDesc()
|
||||
: EnumDescriptor("PlayerActions")
|
||||
{
|
||||
addPair(CHARACTER_FORWARD, "CharacterForward");
|
||||
addPair(CHARACTER_BACKWARD, "CharacterBackward");
|
||||
addPair(CHARACTER_LEFT, "CharacterLeft");
|
||||
addPair(CHARACTER_RIGHT, "CharacterRight");
|
||||
addPair(CHARACTER_JUMP, "CharacterJump");
|
||||
}
|
||||
} // Namespace Reflection
|
||||
|
||||
} // Namespace Aya
|
||||
228
engine/app/src/DataModel/ContextActionService.hpp
Normal file
228
engine/app/src/DataModel/ContextActionService.hpp
Normal file
@@ -0,0 +1,228 @@
|
||||
|
||||
//
|
||||
// ContextActionService.h
|
||||
// App
|
||||
//
|
||||
// Created by Ben Tkacheff on 1/17/13.
|
||||
//
|
||||
// This service is used to figure out what actions a local player can do (right now this is just for tools, but could expand to several functions)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Utility/TextureId.hpp"
|
||||
#include "Utility/UDim.hpp"
|
||||
|
||||
#include "GUI/GuiEvent.hpp"
|
||||
|
||||
#include "Tree/Service.hpp"
|
||||
|
||||
#include "Script/ThreadRef.hpp"
|
||||
#include "DataModel/InputObject.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
namespace Network
|
||||
{
|
||||
class Player;
|
||||
}
|
||||
|
||||
class ModelInstance;
|
||||
class Tool;
|
||||
class GuiButton;
|
||||
|
||||
struct BoundFunctionData
|
||||
{
|
||||
std::string title;
|
||||
std::string description;
|
||||
std::string image;
|
||||
UDim2 position;
|
||||
bool hasTouchButton;
|
||||
|
||||
shared_ptr<const Reflection::Tuple> inputTypes;
|
||||
boost::function<void(shared_ptr<Reflection::Tuple>)> luaFunction;
|
||||
|
||||
weak_ptr<InputObject> lastInput;
|
||||
|
||||
BoundFunctionData()
|
||||
: image("")
|
||||
, inputTypes()
|
||||
, title("")
|
||||
, description("")
|
||||
, position()
|
||||
, hasTouchButton(false)
|
||||
{
|
||||
}
|
||||
|
||||
BoundFunctionData(shared_ptr<const Reflection::Tuple> newInputTypes)
|
||||
: inputTypes(newInputTypes)
|
||||
, image("")
|
||||
, title("")
|
||||
, description("")
|
||||
, position()
|
||||
, hasTouchButton(false)
|
||||
{
|
||||
}
|
||||
|
||||
BoundFunctionData(
|
||||
shared_ptr<const Reflection::Tuple> newInputTypes, boost::function<void(shared_ptr<Reflection::Tuple>)> newFunction, bool touchButton)
|
||||
: inputTypes(newInputTypes)
|
||||
, image("")
|
||||
, title("")
|
||||
, description("")
|
||||
, position()
|
||||
, luaFunction(newFunction)
|
||||
, hasTouchButton(touchButton)
|
||||
{
|
||||
}
|
||||
|
||||
BoundFunctionData(boost::function<void(shared_ptr<Reflection::Tuple>)> newFunction, bool touchButton)
|
||||
: inputTypes()
|
||||
, image("")
|
||||
, title("")
|
||||
, description("")
|
||||
, position()
|
||||
, luaFunction(newFunction)
|
||||
, hasTouchButton(touchButton)
|
||||
{
|
||||
}
|
||||
|
||||
friend bool operator==(const BoundFunctionData& lhs, const BoundFunctionData& rhs)
|
||||
{
|
||||
if (lhs.inputTypes && rhs.inputTypes)
|
||||
{
|
||||
return (lhs.inputTypes.get() == rhs.inputTypes.get()) && (lhs.image == rhs.image) && (lhs.title == rhs.title) &&
|
||||
(lhs.description == rhs.description) && (lhs.position == rhs.position) && (lhs.hasTouchButton == rhs.hasTouchButton);
|
||||
}
|
||||
|
||||
return (lhs.image == rhs.image) && (lhs.title == rhs.title) && (lhs.description == rhs.description) && (lhs.position == rhs.position) &&
|
||||
(lhs.hasTouchButton == rhs.hasTouchButton);
|
||||
}
|
||||
};
|
||||
|
||||
typedef boost::unordered_map<std::string, BoundFunctionData> FunctionMap;
|
||||
typedef std::vector<std::pair<std::string, BoundFunctionData>> FunctionVector;
|
||||
|
||||
typedef std::pair<boost::function<void(shared_ptr<Instance>)>, boost::function<void(std::string)>> FunctionPair;
|
||||
typedef boost::unordered_map<std::string, FunctionPair> FunctionPairMap;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CHARACTER_FORWARD = 0,
|
||||
CHARACTER_BACKWARD = 1,
|
||||
CHARACTER_LEFT = 2,
|
||||
CHARACTER_RIGHT = 3,
|
||||
CHARACTER_JUMP = 4
|
||||
} PlayerActionType;
|
||||
|
||||
extern const char* const sContextActionService;
|
||||
class ContextActionService
|
||||
: public DescribedNonCreatable<ContextActionService, Instance, sContextActionService>
|
||||
, public Service
|
||||
{
|
||||
public:
|
||||
ContextActionService();
|
||||
|
||||
Aya::signal<void(shared_ptr<Instance>)> equippedToolSignal;
|
||||
Aya::signal<void(shared_ptr<Instance>)> unequippedToolSignal;
|
||||
|
||||
Aya::signal<void(std::string, std::string, shared_ptr<const Reflection::ValueTable>)> boundActionChangedSignal;
|
||||
Aya::signal<void(std::string, bool, shared_ptr<const Reflection::ValueTable>)> boundActionAddedSignal;
|
||||
Aya::signal<void(std::string, shared_ptr<const Reflection::ValueTable>)> boundActionRemovedSignal;
|
||||
|
||||
Aya::signal<void(std::string)> getActionButtonSignal;
|
||||
Aya::signal<void(std::string, shared_ptr<Instance>)> actionButtonFoundSignal;
|
||||
|
||||
std::string getCurrentLocalToolIcon();
|
||||
|
||||
void bindCoreActionForInputTypes(
|
||||
const std::string actionName, Lua::WeakFunctionRef functionToBind, bool createTouchButton, shared_ptr<const Reflection::Tuple> hotkeys);
|
||||
void unbindCoreAction(const std::string actionName);
|
||||
|
||||
void bindActionForInputTypes(
|
||||
const std::string actionName, Lua::WeakFunctionRef functionToBind, bool createTouchButton, shared_ptr<const Reflection::Tuple> hotkeys);
|
||||
void unbindAction(const std::string actionName);
|
||||
void unbindAll();
|
||||
|
||||
void bindActivate(InputObject::UserInputType inputType, KeyCode keyCode);
|
||||
void unbindActivate(InputObject::UserInputType inputType, KeyCode keyCode);
|
||||
|
||||
void setTitleForAction(const std::string actionName, std::string title);
|
||||
void setDescForAction(const std::string actionName, std::string description);
|
||||
void setImageForAction(const std::string actionName, std::string image);
|
||||
void setPositionForAction(const std::string actionName, UDim2 position);
|
||||
|
||||
void getButton(
|
||||
const std::string actionName, boost::function<void(shared_ptr<Instance>)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
|
||||
shared_ptr<const Reflection::ValueTable> getBoundCoreActionData(const std::string actionName);
|
||||
shared_ptr<const Reflection::ValueTable> getBoundActionData(const std::string actionName);
|
||||
shared_ptr<const Reflection::ValueTable> getAllBoundActionData();
|
||||
|
||||
void fireActionButtonFoundSignal(const std::string actionName, shared_ptr<Instance> actionButton);
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Instance
|
||||
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Proccessing of input
|
||||
//
|
||||
GuiResponse processCoreBindings(const shared_ptr<InputObject>& inputObject);
|
||||
GuiResponse processDevBindings(const shared_ptr<InputObject>& inputObject, bool menuIsOpen);
|
||||
|
||||
void callFunction(boost::function<void(shared_ptr<Reflection::Tuple>)> luaFunction, const std::string actionName,
|
||||
const InputObject::UserInputState state, const shared_ptr<Instance> inputObject);
|
||||
void callFunction(const std::string actionName, const InputObject::UserInputState state, const shared_ptr<Instance> inputObject);
|
||||
|
||||
protected:
|
||||
Aya::signals::scoped_connection characterChildAddConnection;
|
||||
Aya::signals::scoped_connection characterChildRemoveConnection;
|
||||
Aya::signals::scoped_connection localPlayerAddConnection;
|
||||
|
||||
private:
|
||||
typedef DescribedNonCreatable<ContextActionService, Instance, sContextActionService> Super;
|
||||
|
||||
// these are used for user context binds
|
||||
FunctionMap functionMap;
|
||||
FunctionVector functionVector;
|
||||
|
||||
// these are used for Aya context binds (For Aya menu, etc.)
|
||||
FunctionMap coreFunctionMap;
|
||||
FunctionVector coreFunctionVector;
|
||||
|
||||
FunctionPairMap yieldFunctionMap;
|
||||
|
||||
// used for binding activate (used to simulate mouse clicks on different inputs
|
||||
std::string activateGuid;
|
||||
boost::unordered_map<InputObject*, float> lastZPositionsForActivate;
|
||||
|
||||
Tool* getCurrentLocalTool();
|
||||
Tool* isTool(shared_ptr<Instance> instance);
|
||||
|
||||
void checkForToolRemoval(shared_ptr<Instance> removedChildOfCharacter);
|
||||
void checkForNewTool(shared_ptr<Instance> newChildOfCharacter);
|
||||
|
||||
void disconnectAllCharacterConnections();
|
||||
void localCharacterAdded(shared_ptr<Instance> character);
|
||||
void setupLocalCharacterConnections(ModelInstance* character);
|
||||
|
||||
void checkForLocalPlayer(shared_ptr<Instance> newPlayer);
|
||||
void setupLocalPlayerConnections(Aya::Network::Player* localPlayer);
|
||||
|
||||
void bindActionInternal(const std::string actionName, Lua::WeakFunctionRef functionToBind, bool createTouchButton,
|
||||
shared_ptr<const Reflection::Tuple> hotkeys, FunctionMap& funcMap, FunctionVector& funcVector);
|
||||
|
||||
FunctionMap::iterator findAction(const std::string actionName);
|
||||
|
||||
GuiResponse tryProcess(shared_ptr<InputObject> inputObject, FunctionVector& funcVector, bool menuIsOpen);
|
||||
|
||||
void processActivateAction(const shared_ptr<InputObject>& inputObject);
|
||||
|
||||
void fireBoundActionChangedSignal(FunctionMap::iterator iter, const std::string& changeName);
|
||||
void checkForInputOverride(const Reflection::Variant& newInputType, const FunctionVector& funcVector);
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
33
engine/app/src/DataModel/CornerWedgeInstance.cpp
Normal file
33
engine/app/src/DataModel/CornerWedgeInstance.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
|
||||
|
||||
#include "DataModel/CornerWedgeInstance.hpp"
|
||||
#include "World/CornerWedgePoly.hpp"
|
||||
#include "World/Primitive.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
const char* const sCornerWedge = "CornerWedgePart";
|
||||
|
||||
using namespace Reflection;
|
||||
|
||||
const char* category_CornerWedge = "Part ";
|
||||
static const Vector3 InitialCornerWedgePartSize = Vector3(2.0, 2.0, 2.0);
|
||||
|
||||
CornerWedgeInstance::CornerWedgeInstance()
|
||||
: DescribedCreatable<CornerWedgeInstance, PartInstance, sCornerWedge>(InitialCornerWedgePartSize)
|
||||
{
|
||||
setName("CornerWedge");
|
||||
Primitive* myPrim = this->getPartPrimitive();
|
||||
myPrim->setGeometryType(Geometry::GEOMETRY_CORNERWEDGE);
|
||||
myPrim->setSurfaceType(NORM_X_NEG, NO_SURFACE);
|
||||
myPrim->setSurfaceType(NORM_Y_NEG, NO_SURFACE);
|
||||
myPrim->setSurfaceType(NORM_Y, NO_SURFACE);
|
||||
|
||||
shouldRenderSetDirty();
|
||||
}
|
||||
|
||||
CornerWedgeInstance::~CornerWedgeInstance() {}
|
||||
|
||||
|
||||
} // namespace Aya
|
||||
26
engine/app/src/DataModel/CornerWedgeInstance.hpp
Normal file
26
engine/app/src/DataModel/CornerWedgeInstance.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
#include "DataModel/BasicPartInstance.hpp"
|
||||
#include "Reflection/Reflection.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
extern const char* const sCornerWedge;
|
||||
|
||||
class CornerWedgeInstance : public DescribedCreatable<CornerWedgeInstance, PartInstance, sCornerWedge>
|
||||
{
|
||||
public:
|
||||
CornerWedgeInstance();
|
||||
~CornerWedgeInstance();
|
||||
|
||||
/*override*/ virtual PartType getPartType() const
|
||||
{
|
||||
return CORNERWEDGE_PART;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
26
engine/app/src/DataModel/CustomEvent.cpp
Normal file
26
engine/app/src/DataModel/CustomEvent.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
|
||||
#include "DataModel/CustomEvent.hpp"
|
||||
#include "Reflection/Reflection.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
const char* const sCustomEvent = "CustomEvent";
|
||||
|
||||
|
||||
Reflection::PropDescriptor<CustomEvent, float> CustomEvent::prop_PersistedCurrentValue("PersistedCurrentValue", category_Data,
|
||||
&CustomEvent::getPersistedCurrentValue, &CustomEvent::setPersistedCurrentValue, Reflection::PropertyDescriptor::STREAMING, Security::None);
|
||||
|
||||
Reflection::BoundFuncDesc<CustomEvent, void(float)> CustomEvent::func_SetValue(&CustomEvent::setCurrentValue, "SetValue", "newValue", Security::None);
|
||||
|
||||
Reflection::BoundFuncDesc<CustomEvent, shared_ptr<const Instances>()> CustomEvent::func_GetAttachedReceivers(
|
||||
&CustomEvent::getAttachedReceivers, "GetAttachedReceivers", Security::None);
|
||||
|
||||
Reflection::EventDesc<CustomEvent, void(shared_ptr<Instance>)> CustomEvent::event_ReceiverConnected(
|
||||
&CustomEvent::receiverConnected, "ReceiverConnected", "receiver", Security::None);
|
||||
|
||||
Reflection::EventDesc<CustomEvent, void(shared_ptr<Instance>)> CustomEvent::event_ReceiverDisconnected(
|
||||
&CustomEvent::receiverDisconnected, "ReceiverDisconnected", "receiver", Security::None);
|
||||
REFLECTION_END();
|
||||
} // namespace Aya
|
||||
165
engine/app/src/DataModel/CustomEvent.hpp
Normal file
165
engine/app/src/DataModel/CustomEvent.hpp
Normal file
@@ -0,0 +1,165 @@
|
||||
#pragma once
|
||||
|
||||
#include "signal.hpp"
|
||||
#include "DataModel/CustomEventReceiver.hpp"
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
#include "DataModel/ModelInstance.hpp"
|
||||
#include "DataModel/CollectionService.hpp"
|
||||
#include "Tree/Instance.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
extern const char* const sCustomEvent;
|
||||
class CustomEvent : public DescribedCreatable<CustomEvent, Instance, sCustomEvent>
|
||||
{
|
||||
private:
|
||||
typedef DescribedCreatable<CustomEvent, Instance, sCustomEvent> Super;
|
||||
typedef std::list<weak_ptr<CustomEventReceiver>> ReceiverList;
|
||||
|
||||
// prevent copy and assign: this class does sensitive pointer
|
||||
// management, which would be complicated by allowing copies/assigns.
|
||||
CustomEvent(const CustomEvent& other);
|
||||
CustomEvent& operator=(const CustomEvent& other);
|
||||
|
||||
ReceiverList receivers;
|
||||
float currentValue;
|
||||
|
||||
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
|
||||
{
|
||||
if (oldProvider)
|
||||
oldProvider->create<CollectionService>()->removeInstance(shared_from(this));
|
||||
|
||||
Super::onServiceProvider(oldProvider, newProvider);
|
||||
|
||||
if (newProvider)
|
||||
{
|
||||
newProvider->create<CollectionService>()->addInstance(shared_from(this));
|
||||
}
|
||||
else
|
||||
{
|
||||
ReceiverList copy = receivers;
|
||||
for (ReceiverList::iterator itr = copy.begin(); itr != copy.end(); ++itr)
|
||||
{
|
||||
shared_ptr<CustomEventReceiver> receiver = itr->lock();
|
||||
if (receiver)
|
||||
{
|
||||
receiver->setSource(NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
CustomEvent()
|
||||
: Super(sCustomEvent)
|
||||
, currentValue(0)
|
||||
{
|
||||
}
|
||||
|
||||
// The persisted current value is just for storage -- it is set from the
|
||||
// SetValue callback, and setPersistedCurrentValue _WILL_NOT_ cause
|
||||
// receivers to get a value update.
|
||||
static Reflection::PropDescriptor<CustomEvent, float> prop_PersistedCurrentValue;
|
||||
static Reflection::BoundFuncDesc<CustomEvent, void(float)> func_SetValue;
|
||||
static Reflection::BoundFuncDesc<CustomEvent, shared_ptr<const Instances>()> func_GetAttachedReceivers;
|
||||
static Reflection::EventDesc<CustomEvent, void(shared_ptr<Instance>)> event_ReceiverConnected;
|
||||
static Reflection::EventDesc<CustomEvent, void(shared_ptr<Instance>)> event_ReceiverDisconnected;
|
||||
|
||||
// signals are public for testing only
|
||||
Aya::signal<void(shared_ptr<Instance>)> receiverConnected;
|
||||
Aya::signal<void(shared_ptr<Instance>)> receiverDisconnected;
|
||||
|
||||
/*override*/ virtual bool askSetParent(const Instance* instance) const
|
||||
{
|
||||
return Instance::fastDynamicCast<PartInstance>(instance) != NULL;
|
||||
}
|
||||
|
||||
/*override*/ virtual bool askForbidChild(const Instance* instance) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
float getPersistedCurrentValue() const
|
||||
{
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
// This method is intended for serialization only, it should not be involved
|
||||
// with triggering valueChanged events on receivers.
|
||||
void setPersistedCurrentValue(float newValue)
|
||||
{
|
||||
currentValue = G3D::clamp(newValue, 0, 1);
|
||||
}
|
||||
|
||||
void setCurrentValue(float newValue)
|
||||
{
|
||||
currentValue = G3D::clamp(newValue, 0, 1);
|
||||
raiseChanged(prop_PersistedCurrentValue);
|
||||
for (ReceiverList::iterator itr = receivers.begin(); itr != receivers.end(); ++itr)
|
||||
{
|
||||
shared_ptr<CustomEventReceiver> receiver = itr->lock();
|
||||
if (receiver && receiver->getCurrentValue() != currentValue)
|
||||
{
|
||||
receiver->sourceValueChanged(currentValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<const Instances> getAttachedReceivers()
|
||||
{
|
||||
shared_ptr<Instances> result(new Instances);
|
||||
|
||||
for (ReceiverList::iterator itr = receivers.begin(); itr != receivers.end(); ++itr)
|
||||
{
|
||||
shared_ptr<CustomEventReceiver> receiver = itr->lock();
|
||||
if (receiver)
|
||||
{
|
||||
result->push_back(receiver);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void addReceiver(CustomEventReceiver* receiver)
|
||||
{
|
||||
bool found = false;
|
||||
for (ReceiverList::iterator itr = receivers.begin(); itr != receivers.end() && !found; ++itr)
|
||||
{
|
||||
shared_ptr<CustomEventReceiver> list_receiver = itr->lock();
|
||||
if (list_receiver.get() == receiver)
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
receivers.push_back(weak_ptr<CustomEventReceiver>(shared_from(receiver)));
|
||||
// send event only after internal state has been updated
|
||||
receiverConnected(shared_from(receiver));
|
||||
}
|
||||
}
|
||||
|
||||
void removeReceiver(CustomEventReceiver* receiver)
|
||||
{
|
||||
ReceiverList::iterator foundIterator;
|
||||
bool found = false;
|
||||
for (ReceiverList::iterator itr = receivers.begin(); itr != receivers.end() && !found; ++itr)
|
||||
{
|
||||
shared_ptr<CustomEventReceiver> list_receiver = itr->lock();
|
||||
if (list_receiver.get() == receiver)
|
||||
{
|
||||
foundIterator = itr;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
{
|
||||
receivers.erase(foundIterator);
|
||||
// send event only after internal state has been updated
|
||||
receiverDisconnected(shared_from(receiver));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
100
engine/app/src/DataModel/CustomEventReceiver.cpp
Normal file
100
engine/app/src/DataModel/CustomEventReceiver.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
|
||||
|
||||
#include "DataModel/CustomEventReceiver.hpp"
|
||||
#include "DataModel/CustomEvent.hpp"
|
||||
#include "DataModel/CollectionService.hpp"
|
||||
#include "Reflection/Reflection.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
const char* const sCustomEventReceiver = "CustomEventReceiver";
|
||||
|
||||
Reflection::RefPropDescriptor<CustomEventReceiver, Instance> CustomEventReceiver::prop_Source("Source", category_Data,
|
||||
&CustomEventReceiver::getSource, &CustomEventReceiver::setSource, Reflection::PropertyDescriptor::STANDARD, Security::None);
|
||||
|
||||
Reflection::EventDesc<CustomEventReceiver, void(float)> CustomEventReceiver::event_SourceValueChanged(
|
||||
&CustomEventReceiver::sourceValueChanged, "SourceValueChanged", "newValue", Security::None);
|
||||
|
||||
Reflection::BoundFuncDesc<CustomEventReceiver, float()> CustomEventReceiver::func_GetCurrentValue(
|
||||
&CustomEventReceiver::getCurrentValue, "GetCurrentValue", Security::None);
|
||||
|
||||
Reflection::EventDesc<CustomEventReceiver, void(shared_ptr<Instance>)> CustomEventReceiver::event_EventConnected(
|
||||
&CustomEventReceiver::eventConnected, "EventConnected", "event", Security::None);
|
||||
|
||||
Reflection::EventDesc<CustomEventReceiver, void(shared_ptr<Instance>)> CustomEventReceiver::event_EventDisconnected(
|
||||
&CustomEventReceiver::eventDisconnected, "EventDisconnected", "event", Security::None);
|
||||
REFLECTION_END();
|
||||
|
||||
void CustomEventReceiver::setSource(Instance* sourceEvent)
|
||||
{
|
||||
shared_ptr<CustomEvent> currentSourceEvent = this->sourceEvent.lock();
|
||||
if (sourceEvent == currentSourceEvent.get())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// disconnect from old sourceEvent if this was connected before
|
||||
if (currentSourceEvent.get() != NULL)
|
||||
{
|
||||
currentSourceEvent->removeReceiver(this);
|
||||
this->sourceEvent.reset();
|
||||
// send event only after internal state has been updated
|
||||
eventDisconnected(currentSourceEvent);
|
||||
}
|
||||
|
||||
// connect to the new sourceEvent if it is not NULL
|
||||
if (sourceEvent != NULL)
|
||||
{
|
||||
CustomEvent* castSource = Instance::fastDynamicCast<CustomEvent>(sourceEvent);
|
||||
debugAssert(castSource != NULL);
|
||||
if (castSource != NULL)
|
||||
{
|
||||
castSource->addReceiver(this);
|
||||
this->sourceEvent = shared_from(castSource);
|
||||
if (getCurrentValue() != castSource->getPersistedCurrentValue())
|
||||
{
|
||||
sourceValueChanged(castSource->getPersistedCurrentValue());
|
||||
}
|
||||
// send event only after internal state is updated
|
||||
eventConnected(shared_from(castSource));
|
||||
}
|
||||
}
|
||||
|
||||
if (sourceEvent == NULL && getCurrentValue() != 0)
|
||||
{
|
||||
sourceValueChanged(0);
|
||||
}
|
||||
|
||||
raisePropertyChanged(prop_Source);
|
||||
}
|
||||
|
||||
void CustomEventReceiver::onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
|
||||
{
|
||||
if (oldProvider)
|
||||
oldProvider->create<CollectionService>()->removeInstance(shared_from(this));
|
||||
|
||||
Super::onServiceProvider(oldProvider, newProvider);
|
||||
|
||||
if (newProvider)
|
||||
{
|
||||
newProvider->create<CollectionService>()->addInstance(shared_from(this));
|
||||
}
|
||||
else
|
||||
{
|
||||
setSource(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace Aya
|
||||
|
||||
|
||||
// Randomized Locations for hackflags
|
||||
namespace Aya
|
||||
{
|
||||
namespace Security
|
||||
{
|
||||
unsigned int hackFlag4 = 0;
|
||||
};
|
||||
}; // namespace Aya
|
||||
84
engine/app/src/DataModel/CustomEventReceiver.hpp
Normal file
84
engine/app/src/DataModel/CustomEventReceiver.hpp
Normal file
@@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
|
||||
#include "signal.hpp"
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
#include "DataModel/ModelInstance.hpp"
|
||||
#include "Tree/Instance.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class CustomEvent;
|
||||
|
||||
extern const char* const sCustomEventReceiver;
|
||||
class CustomEventReceiver : public DescribedCreatable<CustomEventReceiver, Instance, sCustomEventReceiver>
|
||||
{
|
||||
private:
|
||||
typedef DescribedCreatable<CustomEventReceiver, Instance, sCustomEventReceiver> Super;
|
||||
|
||||
// prevent copy and assign: this class does sensitive pointer
|
||||
// management, which would be complicated by allowing copies/assigns.
|
||||
CustomEventReceiver(const CustomEventReceiver& other);
|
||||
CustomEventReceiver& operator=(const CustomEventReceiver& other);
|
||||
|
||||
weak_ptr<CustomEvent> sourceEvent;
|
||||
Aya::signals::scoped_connection sourceValueChangedConnection;
|
||||
float lastReceivedValue;
|
||||
|
||||
public:
|
||||
// public for interoperation with CustomEvent
|
||||
Aya::signal<void(float)> sourceValueChanged;
|
||||
// connection signals public for testing
|
||||
Aya::signal<void(shared_ptr<Instance>)> eventConnected;
|
||||
Aya::signal<void(shared_ptr<Instance>)> eventDisconnected;
|
||||
|
||||
CustomEventReceiver()
|
||||
: Super(sCustomEventReceiver)
|
||||
, lastReceivedValue(0)
|
||||
{
|
||||
sourceValueChangedConnection = sourceValueChanged.connect(boost::bind(&CustomEventReceiver::setCurrentValue, this, _1));
|
||||
}
|
||||
|
||||
static Reflection::RefPropDescriptor<CustomEventReceiver, Instance> prop_Source;
|
||||
static Reflection::EventDesc<CustomEventReceiver, void(float)> event_SourceValueChanged;
|
||||
static Reflection::BoundFuncDesc<CustomEventReceiver, float()> func_GetCurrentValue;
|
||||
static Reflection::EventDesc<CustomEventReceiver, void(shared_ptr<Instance>)> event_EventConnected;
|
||||
static Reflection::EventDesc<CustomEventReceiver, void(shared_ptr<Instance>)> event_EventDisconnected;
|
||||
|
||||
/*override*/ bool askSetParent(const Instance* instance) const
|
||||
{
|
||||
return Instance::fastDynamicCast<PartInstance>(instance) != NULL;
|
||||
}
|
||||
|
||||
/*override*/ bool askForbidChild(const Instance* instance) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// needs to be forward declared because it depends on CustomEvent
|
||||
/*override*/ // virtual void onAncestorChanged(const AncestorChanged& event);
|
||||
|
||||
// should only be used for serialization
|
||||
Instance* const getSource() const
|
||||
{
|
||||
return (Instance*)sourceEvent.lock().get();
|
||||
}
|
||||
// should only be used for serialization
|
||||
void setSource(Instance* sourceEvent);
|
||||
|
||||
void setCurrentValue(float newValue)
|
||||
{
|
||||
AYAASSERT(newValue != lastReceivedValue);
|
||||
lastReceivedValue = newValue;
|
||||
}
|
||||
|
||||
float getCurrentValue()
|
||||
{
|
||||
return lastReceivedValue;
|
||||
}
|
||||
|
||||
protected:
|
||||
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
275
engine/app/src/DataModel/CustomParticleEmitter.cpp
Normal file
275
engine/app/src/DataModel/CustomParticleEmitter.cpp
Normal file
@@ -0,0 +1,275 @@
|
||||
|
||||
#include "DataModel/CustomParticleEmitter.hpp"
|
||||
|
||||
#define CAT_VIS category_Appearance
|
||||
#define CAT_EMISSION "Emission"
|
||||
#define CAT_MOTION "Motion"
|
||||
#define CAT_PARTICLE_BEHAVIOR "Particles"
|
||||
|
||||
DYNAMIC_FASTFLAGVARIABLE(CustomEmitterInstanceEnabled, false)
|
||||
DYNAMIC_FASTFLAGVARIABLE(EnableParticleDrag, false)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
const char* const sParticleEmitter = "ParticleEmitter";
|
||||
|
||||
CustomParticleEmitter::CustomParticleEmitter()
|
||||
: DescribedCreatable<CustomParticleEmitter, Instance, sParticleEmitter>("ParticleEmitter")
|
||||
, texture("ayaasset://textures/particles/sparkles_main.jxl")
|
||||
, color(Color3(1, 1, 1))
|
||||
, transparency(0)
|
||||
, size(1)
|
||||
, lightEmission(0)
|
||||
, enabled(true)
|
||||
, rate(20)
|
||||
, speed(5, 5)
|
||||
, spread(0)
|
||||
, rotation(0, 0)
|
||||
, rotSpeed(0, 0)
|
||||
, lifetime(5, 10)
|
||||
, accel(0, 0, 0)
|
||||
, zOffset(0)
|
||||
, lockedToLocalSpace(false)
|
||||
, dampening(0)
|
||||
, velocityInheritance(0)
|
||||
, emissionDirection(Aya::NORM_Y)
|
||||
{
|
||||
}
|
||||
|
||||
CustomParticleEmitter::~CustomParticleEmitter() {}
|
||||
|
||||
|
||||
|
||||
// static Reflection::BoundProp<> prop_( "", CAT_VIS, &ParticleEmitter::, &ParticleEmitter:: );
|
||||
|
||||
Reflection::PropDescriptor<CustomParticleEmitter, TextureId> CustomParticleEmitter::prop_texture(
|
||||
"Texture", CAT_VIS, &CustomParticleEmitter::getTexture, &CustomParticleEmitter::setTexture);
|
||||
Reflection::PropDescriptor<CustomParticleEmitter, ColorSequence> CustomParticleEmitter::prop_color(
|
||||
"Color", CAT_VIS, &CustomParticleEmitter::getColor, &CustomParticleEmitter::setColor);
|
||||
Reflection::PropDescriptor<CustomParticleEmitter, NumberSequence> CustomParticleEmitter::prop_transp(
|
||||
"Transparency", CAT_VIS, &CustomParticleEmitter::getTransparency, &CustomParticleEmitter::setTransparency);
|
||||
Reflection::PropDescriptor<CustomParticleEmitter, NumberSequence> CustomParticleEmitter::prop_size(
|
||||
"Size", CAT_VIS, &CustomParticleEmitter::getSize, &CustomParticleEmitter::setSize);
|
||||
|
||||
Reflection::PropDescriptor<CustomParticleEmitter, bool> CustomParticleEmitter::prop_enabled(
|
||||
"Enabled", CAT_EMISSION, &CustomParticleEmitter::getEnabled, &CustomParticleEmitter::setEnabled);
|
||||
Reflection::PropDescriptor<CustomParticleEmitter, float> CustomParticleEmitter::prop_lightEmission(
|
||||
"LightEmission", CAT_VIS, &CustomParticleEmitter::getLightEmission, &CustomParticleEmitter::setLightEmission);
|
||||
Reflection::PropDescriptor<CustomParticleEmitter, float> CustomParticleEmitter::prop_rate(
|
||||
"Rate", CAT_EMISSION, &CustomParticleEmitter::getRate, &CustomParticleEmitter::setRate);
|
||||
Reflection::PropDescriptor<CustomParticleEmitter, NumberRange> CustomParticleEmitter::prop_speed(
|
||||
"Speed", CAT_EMISSION, &CustomParticleEmitter::getSpeed, &CustomParticleEmitter::setSpeed);
|
||||
Reflection::PropDescriptor<CustomParticleEmitter, float> CustomParticleEmitter::prop_spread(
|
||||
"VelocitySpread", CAT_EMISSION, &CustomParticleEmitter::getSpread, &CustomParticleEmitter::setSpread);
|
||||
Reflection::PropDescriptor<CustomParticleEmitter, NumberRange> CustomParticleEmitter::prop_rotation(
|
||||
"Rotation", CAT_EMISSION, &CustomParticleEmitter::getRotation, &CustomParticleEmitter::setRotation);
|
||||
Reflection::PropDescriptor<CustomParticleEmitter, NumberRange> CustomParticleEmitter::prop_rotSpeed(
|
||||
"RotSpeed", CAT_EMISSION, &CustomParticleEmitter::getRotSpeed, &CustomParticleEmitter::setRotSpeed);
|
||||
Reflection::PropDescriptor<CustomParticleEmitter, NumberRange> CustomParticleEmitter::prop_lifetime(
|
||||
"Lifetime", CAT_EMISSION, &CustomParticleEmitter::getLifetime, &CustomParticleEmitter::setLifetime);
|
||||
Reflection::PropDescriptor<CustomParticleEmitter, Vector3> CustomParticleEmitter::prop_accel(
|
||||
"Acceleration", CAT_MOTION, &CustomParticleEmitter::getAccel, &CustomParticleEmitter::setAccel);
|
||||
Reflection::PropDescriptor<CustomParticleEmitter, float> CustomParticleEmitter::prop_zOffset(
|
||||
"ZOffset", CAT_VIS, &CustomParticleEmitter::getZOffset, &CustomParticleEmitter::setZOffset);
|
||||
Reflection::PropDescriptor<CustomParticleEmitter, float> CustomParticleEmitter::prop_velocityInheritance(
|
||||
"VelocityInheritance", CAT_PARTICLE_BEHAVIOR, &CustomParticleEmitter::getVelocityInheritance, &CustomParticleEmitter::setVelocityInheritance);
|
||||
Reflection::PropDescriptor<CustomParticleEmitter, float> CustomParticleEmitter::prop_dampening(
|
||||
"Drag", CAT_PARTICLE_BEHAVIOR, &CustomParticleEmitter::getDampening, &CustomParticleEmitter::setDampening);
|
||||
Reflection::PropDescriptor<CustomParticleEmitter, bool> CustomParticleEmitter::prop_lockedToLocalSpace(
|
||||
"LockedToPart", CAT_PARTICLE_BEHAVIOR, &CustomParticleEmitter::getLockedToLocalSpace, &CustomParticleEmitter::setLockedToLocalSpace);
|
||||
Reflection::EnumPropDescriptor<CustomParticleEmitter, NormalId> CustomParticleEmitter::prop_emissionDirection(
|
||||
"EmissionDirection", CAT_EMISSION, &CustomParticleEmitter::getEmissionDirection, &CustomParticleEmitter::setEmissionDirection);
|
||||
|
||||
Reflection::BoundFuncDesc<CustomParticleEmitter, void(int)> CustomParticleEmitter::desc_burst(
|
||||
&CustomParticleEmitter::requestBurst, "Emit", "particleCount", 16, Security::None);
|
||||
|
||||
Reflection::RemoteEventDesc<CustomParticleEmitter, void(int)> CustomParticleEmitter::event_onEmitRequested(&CustomParticleEmitter::onEmitRequested,
|
||||
"OnEmitRequested", "particleCount", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
|
||||
REFLECTION_END();
|
||||
|
||||
|
||||
void CustomParticleEmitter::setEnabled(bool v)
|
||||
{
|
||||
if (enabled == v)
|
||||
return;
|
||||
enabled = v;
|
||||
raisePropertyChanged(prop_enabled);
|
||||
}
|
||||
|
||||
void CustomParticleEmitter::setLightEmission(float v)
|
||||
{
|
||||
if (lightEmission == v)
|
||||
return;
|
||||
lightEmission = v;
|
||||
raisePropertyChanged(prop_lightEmission);
|
||||
}
|
||||
|
||||
void CustomParticleEmitter::setRate(float v)
|
||||
{
|
||||
if (rate == v)
|
||||
return;
|
||||
rate = v;
|
||||
raisePropertyChanged(prop_rate);
|
||||
}
|
||||
|
||||
void CustomParticleEmitter::setSpeed(const NumberRange& v)
|
||||
{
|
||||
if (speed == v)
|
||||
return;
|
||||
speed = v;
|
||||
raisePropertyChanged(prop_speed);
|
||||
}
|
||||
|
||||
void CustomParticleEmitter::setSpread(float v)
|
||||
{
|
||||
if (spread == v)
|
||||
return;
|
||||
spread = v;
|
||||
raisePropertyChanged(prop_spread);
|
||||
}
|
||||
|
||||
void CustomParticleEmitter::setRotation(const NumberRange& v)
|
||||
{
|
||||
if (rotation == v)
|
||||
return;
|
||||
rotation = v;
|
||||
raisePropertyChanged(prop_rotation);
|
||||
}
|
||||
|
||||
void CustomParticleEmitter::setRotSpeed(const NumberRange& v)
|
||||
{
|
||||
if (rotSpeed == v)
|
||||
return;
|
||||
rotSpeed = v;
|
||||
raisePropertyChanged(prop_rotSpeed);
|
||||
}
|
||||
|
||||
void CustomParticleEmitter::setLifetime(const NumberRange& v)
|
||||
{
|
||||
if (lifetime == v)
|
||||
return;
|
||||
lifetime = v;
|
||||
raisePropertyChanged(prop_lifetime);
|
||||
}
|
||||
|
||||
void CustomParticleEmitter::setAccel(const Vector3& v)
|
||||
{
|
||||
if (accel == v)
|
||||
return;
|
||||
accel = v;
|
||||
raisePropertyChanged(prop_accel);
|
||||
}
|
||||
|
||||
void CustomParticleEmitter::setZOffset(float v)
|
||||
{
|
||||
if (zOffset == v)
|
||||
return;
|
||||
zOffset = v;
|
||||
raisePropertyChanged(prop_zOffset);
|
||||
}
|
||||
|
||||
const TextureId& CustomParticleEmitter::getTexture() const
|
||||
{
|
||||
return texture;
|
||||
}
|
||||
|
||||
void CustomParticleEmitter::setTexture(const TextureId& id)
|
||||
{
|
||||
if (texture == id)
|
||||
return;
|
||||
texture = id;
|
||||
raisePropertyChanged(prop_texture);
|
||||
}
|
||||
|
||||
const ColorSequence& CustomParticleEmitter::getColor() const
|
||||
{
|
||||
return color;
|
||||
}
|
||||
|
||||
void CustomParticleEmitter::setColor(const ColorSequence& val)
|
||||
{
|
||||
if (color == val)
|
||||
return;
|
||||
color = val;
|
||||
raisePropertyChanged(prop_color);
|
||||
}
|
||||
|
||||
const NumberSequence& CustomParticleEmitter::getTransparency() const
|
||||
{
|
||||
return transparency;
|
||||
}
|
||||
|
||||
void CustomParticleEmitter::setTransparency(const NumberSequence& v)
|
||||
{
|
||||
if (transparency == v)
|
||||
return;
|
||||
transparency = v;
|
||||
raisePropertyChanged(prop_transp);
|
||||
}
|
||||
|
||||
const NumberSequence& CustomParticleEmitter::getSize() const
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
void CustomParticleEmitter::setSize(const NumberSequence& val)
|
||||
{
|
||||
if (size == val)
|
||||
return;
|
||||
size = val;
|
||||
raisePropertyChanged(prop_size);
|
||||
}
|
||||
|
||||
void CustomParticleEmitter::setVelocityInheritance(float value)
|
||||
{
|
||||
if (value != velocityInheritance)
|
||||
{
|
||||
velocityInheritance = value;
|
||||
raisePropertyChanged(prop_velocityInheritance);
|
||||
}
|
||||
}
|
||||
|
||||
void CustomParticleEmitter::setDampening(float value)
|
||||
{
|
||||
if (!DFFlag::EnableParticleDrag)
|
||||
{
|
||||
raisePropertyChanged(prop_dampening);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value != dampening)
|
||||
{
|
||||
dampening = value;
|
||||
raisePropertyChanged(prop_dampening);
|
||||
}
|
||||
}
|
||||
|
||||
void CustomParticleEmitter::setLockedToLocalSpace(bool value)
|
||||
{
|
||||
if (value != lockedToLocalSpace)
|
||||
{
|
||||
lockedToLocalSpace = value;
|
||||
raisePropertyChanged(prop_lockedToLocalSpace);
|
||||
}
|
||||
}
|
||||
|
||||
void CustomParticleEmitter::setEmissionDirection(NormalId value)
|
||||
{
|
||||
if (value != emissionDirection)
|
||||
{
|
||||
emissionDirection = value;
|
||||
raisePropertyChanged(prop_emissionDirection);
|
||||
}
|
||||
}
|
||||
|
||||
void CustomParticleEmitter::requestBurst(int value)
|
||||
{
|
||||
event_onEmitRequested.fireAndReplicateEvent(this, value);
|
||||
}
|
||||
|
||||
void CustomParticleEmitter::onAncestorChanged(const AncestorChanged& ev)
|
||||
{
|
||||
Base::onAncestorChanged(ev);
|
||||
}
|
||||
} // namespace Aya
|
||||
178
engine/app/src/DataModel/CustomParticleEmitter.hpp
Normal file
178
engine/app/src/DataModel/CustomParticleEmitter.hpp
Normal file
@@ -0,0 +1,178 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "Tree/Instance.hpp"
|
||||
#include "DataModel/Effect.hpp"
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
#include "Utility/TextureId.hpp"
|
||||
|
||||
#include "DataModel/NumberSequence.hpp"
|
||||
#include "DataModel/ColorSequence.hpp"
|
||||
#include "DataModel/NumberRange.hpp"
|
||||
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
extern const char* const sParticleEmitter;
|
||||
class CustomParticleEmitter
|
||||
: public DescribedCreatable<CustomParticleEmitter, Instance, sParticleEmitter>
|
||||
, public Effect
|
||||
{
|
||||
typedef DescribedCreatable<CustomParticleEmitter, Instance, sParticleEmitter> Base;
|
||||
|
||||
public:
|
||||
CustomParticleEmitter();
|
||||
virtual ~CustomParticleEmitter();
|
||||
|
||||
|
||||
|
||||
static Reflection::PropDescriptor<CustomParticleEmitter, TextureId> prop_texture;
|
||||
static Reflection::PropDescriptor<CustomParticleEmitter, ColorSequence> prop_color;
|
||||
static Reflection::PropDescriptor<CustomParticleEmitter, NumberSequence> prop_transp;
|
||||
static Reflection::PropDescriptor<CustomParticleEmitter, NumberSequence> prop_size;
|
||||
|
||||
static Reflection::PropDescriptor<CustomParticleEmitter, bool> prop_enabled;
|
||||
static Reflection::PropDescriptor<CustomParticleEmitter, float> prop_lightEmission;
|
||||
static Reflection::PropDescriptor<CustomParticleEmitter, float> prop_rate;
|
||||
static Reflection::PropDescriptor<CustomParticleEmitter, NumberRange> prop_speed;
|
||||
static Reflection::PropDescriptor<CustomParticleEmitter, float> prop_spread;
|
||||
static Reflection::PropDescriptor<CustomParticleEmitter, NumberRange> prop_rotation;
|
||||
static Reflection::PropDescriptor<CustomParticleEmitter, NumberRange> prop_rotSpeed;
|
||||
static Reflection::PropDescriptor<CustomParticleEmitter, NumberRange> prop_lifetime;
|
||||
static Reflection::PropDescriptor<CustomParticleEmitter, Vector3> prop_accel;
|
||||
static Reflection::PropDescriptor<CustomParticleEmitter, float> prop_zOffset;
|
||||
static Reflection::PropDescriptor<CustomParticleEmitter, float> prop_velocityInheritance;
|
||||
static Reflection::PropDescriptor<CustomParticleEmitter, float> prop_dampening;
|
||||
static Reflection::PropDescriptor<CustomParticleEmitter, bool> prop_lockedToLocalSpace;
|
||||
static Reflection::EnumPropDescriptor<CustomParticleEmitter, NormalId> prop_emissionDirection;
|
||||
|
||||
static Reflection::RemoteEventDesc<CustomParticleEmitter, void(int)> event_onEmitRequested;
|
||||
|
||||
static Reflection::BoundFuncDesc<CustomParticleEmitter, void(int)> desc_burst;
|
||||
|
||||
bool getEnabled() const
|
||||
{
|
||||
return enabled;
|
||||
}
|
||||
float getLightEmission() const
|
||||
{
|
||||
return lightEmission;
|
||||
}
|
||||
float getRate() const
|
||||
{
|
||||
return rate;
|
||||
}
|
||||
const NumberRange& getSpeed() const
|
||||
{
|
||||
return speed;
|
||||
}
|
||||
float getSpread() const
|
||||
{
|
||||
return spread;
|
||||
}
|
||||
const NumberRange& getRotation() const
|
||||
{
|
||||
return rotation;
|
||||
}
|
||||
const NumberRange& getRotSpeed() const
|
||||
{
|
||||
return rotSpeed;
|
||||
}
|
||||
const NumberRange& getLifetime() const
|
||||
{
|
||||
return lifetime;
|
||||
}
|
||||
const Vector3& getAccel() const
|
||||
{
|
||||
return accel;
|
||||
}
|
||||
float getZOffset() const
|
||||
{
|
||||
return zOffset;
|
||||
}
|
||||
|
||||
void setEnabled(bool v);
|
||||
void setLightEmission(float v);
|
||||
void setRate(float v);
|
||||
void setSpeed(const NumberRange& v);
|
||||
void setSpread(float v);
|
||||
void setRotation(const NumberRange& v);
|
||||
void setRotSpeed(const NumberRange& v);
|
||||
void setLifetime(const NumberRange& v);
|
||||
void setAccel(const Vector3& v);
|
||||
void setZOffset(float v);
|
||||
|
||||
Aya::remote_signal<void(int)> onEmitRequested;
|
||||
|
||||
const TextureId& getTexture() const;
|
||||
void setTexture(const TextureId& id);
|
||||
|
||||
const NumberSequence& getTransparency() const;
|
||||
void setTransparency(const NumberSequence& v);
|
||||
|
||||
const ColorSequence& getColor() const;
|
||||
void setColor(const ColorSequence& val);
|
||||
|
||||
const NumberSequence& getSize() const;
|
||||
void setSize(const NumberSequence& val);
|
||||
|
||||
float getVelocityInheritance() const
|
||||
{
|
||||
return velocityInheritance;
|
||||
}
|
||||
void setVelocityInheritance(float value);
|
||||
|
||||
float getDampening() const
|
||||
{
|
||||
return dampening;
|
||||
}
|
||||
void setDampening(float value);
|
||||
|
||||
bool getLockedToLocalSpace() const
|
||||
{
|
||||
return lockedToLocalSpace;
|
||||
}
|
||||
void setLockedToLocalSpace(bool value);
|
||||
|
||||
void requestBurst(int value);
|
||||
|
||||
NormalId getEmissionDirection() const
|
||||
{
|
||||
return emissionDirection;
|
||||
}
|
||||
void setEmissionDirection(NormalId value);
|
||||
|
||||
private:
|
||||
TextureId texture;
|
||||
ColorSequence color;
|
||||
NumberSequence transparency;
|
||||
NumberSequence size;
|
||||
|
||||
bool enabled;
|
||||
float lightEmission;
|
||||
float rate;
|
||||
NumberRange speed;
|
||||
float spread;
|
||||
NumberRange rotation;
|
||||
NumberRange rotSpeed;
|
||||
NumberRange lifetime;
|
||||
Vector3 accel;
|
||||
float zOffset;
|
||||
float velocityInheritance;
|
||||
float dampening;
|
||||
bool lockedToLocalSpace;
|
||||
|
||||
NormalId emissionDirection;
|
||||
|
||||
virtual bool askSetParent(const Instance* parent) const
|
||||
{
|
||||
return Instance::fastDynamicCast<PartInstance>(parent) != NULL;
|
||||
}
|
||||
virtual bool askAddChild(const Instance* instance) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual void onAncestorChanged(const AncestorChanged& event);
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
8
engine/app/src/DataModel/CylinderMesh.cpp
Normal file
8
engine/app/src/DataModel/CylinderMesh.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
|
||||
|
||||
#include "DataModel/CylinderMesh.hpp"
|
||||
|
||||
using namespace Aya;
|
||||
|
||||
const char* const Aya::sCylinderMesh = "CylinderMesh";
|
||||
15
engine/app/src/DataModel/CylinderMesh.hpp
Normal file
15
engine/app/src/DataModel/CylinderMesh.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BevelMesh.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
extern const char* const sCylinderMesh;
|
||||
class CylinderMesh : public DescribedCreatable<CylinderMesh, BevelMesh, sCylinderMesh>
|
||||
{
|
||||
public:
|
||||
CylinderMesh() {}
|
||||
};
|
||||
} // namespace Aya
|
||||
4325
engine/app/src/DataModel/DataModel.cpp
Normal file
4325
engine/app/src/DataModel/DataModel.cpp
Normal file
File diff suppressed because it is too large
Load Diff
873
engine/app/src/DataModel/DataModel.hpp
Normal file
873
engine/app/src/DataModel/DataModel.hpp
Normal file
@@ -0,0 +1,873 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GUI/GUI.hpp"
|
||||
#include "DataModel/DataModelJob.hpp"
|
||||
#include "DataModel/PhysicsInstructions.hpp"
|
||||
#include "DataModel/Game.hpp"
|
||||
#include "DataModel/InputObject.hpp"
|
||||
#include "Tree/Instance.hpp"
|
||||
#include "Tree/Service.hpp"
|
||||
#include "Utility/GameMode.hpp"
|
||||
#include "Utility/RunStateOwner.hpp"
|
||||
#include "Utility/InsertMode.hpp"
|
||||
#include "Utility/Region2.hpp"
|
||||
#include "Utility/IMetric.hpp"
|
||||
#include "Utility/HeapValue.hpp"
|
||||
#include "Security/FuzzyTokens.hpp"
|
||||
#include "DataModel/DebugMenu.hpp"
|
||||
#include "DataModel/GameBasicSettings.hpp"
|
||||
#include <boost/array.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/unordered_set.hpp>
|
||||
|
||||
class XmlElement;
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class Fonts;
|
||||
class GuiRoot;
|
||||
class GuiItem;
|
||||
class ContentProvider;
|
||||
class Hopper;
|
||||
class PlayerHopper;
|
||||
class StarterPackService;
|
||||
class StarterGuiService;
|
||||
class StarterPlayerService;
|
||||
class CoreGuiService;
|
||||
class Workspace;
|
||||
class Adorn;
|
||||
class Region2;
|
||||
class UserInputService;
|
||||
class ContextActionService;
|
||||
class GuiObject;
|
||||
|
||||
extern const char* const sDataModel;
|
||||
|
||||
static inline void robloxScriptModifiedCheck(Aya::Security::Permissions perm)
|
||||
{
|
||||
#ifndef AYA_STUDIO
|
||||
Aya::Security::Context::current().requirePermission(Aya::Security::RobloxScript);
|
||||
if (perm != Aya::Security::RobloxScript)
|
||||
{
|
||||
throw std::runtime_error("");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
class DataModel
|
||||
: public IMetric
|
||||
, public IDataState
|
||||
, public VerbContainer
|
||||
, public Diagnostics::Countable<DataModel>
|
||||
, public DataModelArbiter
|
||||
, public DescribedNonCreatable<DataModel, ServiceProvider, sDataModel>
|
||||
{
|
||||
public:
|
||||
bool shouldShowLoadingScreen = true;
|
||||
GameBasicSettings::VirtualVersion initialVirtualVersion = GameBasicSettings::VERSION_2016;
|
||||
|
||||
enum CreatorType
|
||||
{
|
||||
CREATOR_USER = 0,
|
||||
CREATOR_GROUP = 1
|
||||
};
|
||||
// genre == GENRE_ALL || (allowedGenres & (1 << (genre-1))) != 0;
|
||||
enum Genre
|
||||
{
|
||||
GENRE_ALL = 0,
|
||||
GENRE_TOWN_AND_CITY = 1,
|
||||
GENRE_FANTASY = 2,
|
||||
GENRE_SCI_FI = 3,
|
||||
GENRE_NINJA = 4,
|
||||
GENRE_SCARY = 5,
|
||||
GENRE_PIRATE = 6,
|
||||
GENRE_ADVENTURE = 7,
|
||||
GENRE_SPORTS = 8,
|
||||
GENRE_FUNNY = 9,
|
||||
GENRE_WILD_WEST = 10,
|
||||
GENRE_WAR = 11,
|
||||
GENRE_SKATE_PARK = 12,
|
||||
GENRE_TUTORIAL = 13,
|
||||
};
|
||||
enum GearGenreSetting
|
||||
{
|
||||
GEAR_GENRE_ALL = 0,
|
||||
GEAR_GENRE_MATCH = 1,
|
||||
};
|
||||
|
||||
//(allowedGearTypes & (1 << gearType)) != 0;
|
||||
enum GearType
|
||||
{
|
||||
GEAR_TYPE_MELEE_WEAPONS = 0,
|
||||
GEAR_TYPE_RANGED_WEAPONS = 1,
|
||||
GEAR_TYPE_EXPLOSIVES = 2,
|
||||
GEAR_TYPE_POWER_UPS = 3,
|
||||
GEAR_TYPE_NAVIGATION_ENHANCERS = 4,
|
||||
GEAR_TYPE_MUSICAL_INSTRUMENTS = 5,
|
||||
GEAR_TYPE_SOCIAL_ITEMS = 6,
|
||||
GEAR_TYPE_BUILDING_TOOLS = 7,
|
||||
GEAR_TYPE_PERSONAL_TRANSPORT = 8,
|
||||
};
|
||||
|
||||
enum RequestShutdownResult
|
||||
{
|
||||
CLOSE_NOT_HANDLED = 0,
|
||||
CLOSE_REQUEST_HANDLED = 1,
|
||||
CLOSE_LOCAL_SAVE = 2,
|
||||
CLOSE_NO_SAVE_NEEDED = 3,
|
||||
};
|
||||
|
||||
void postCreate();
|
||||
|
||||
static Aya::atomic<int> count;
|
||||
std::auto_ptr<Aya::Verb> lockVerb;
|
||||
|
||||
Aya::signal<void(bool)> graphicsQualityShortcutSignal;
|
||||
|
||||
Aya::signal<void()> allowedGearTypeChanged;
|
||||
Aya::signal<void(const shared_ptr<InputObject>& event)> InputObjectProcessed;
|
||||
|
||||
Aya::signal<void()> workspaceLoadedSignal;
|
||||
Aya::signal<void()> gameLoadedSignal;
|
||||
|
||||
Genre getGenre() const
|
||||
{
|
||||
return genre;
|
||||
}
|
||||
void setGenre(Genre genre);
|
||||
|
||||
GearGenreSetting getGearGenreSetting() const
|
||||
{
|
||||
return gearGenreSetting;
|
||||
}
|
||||
void setGear(GearGenreSetting gearGenreSetting, int allowedGearTypes);
|
||||
|
||||
void setVIPServerId(std::string value);
|
||||
std::string getVIPServerId() const;
|
||||
void setVIPServerOwnerId(int value);
|
||||
int getVIPServerOwnerId() const;
|
||||
|
||||
void setRenderGuisActive(bool value)
|
||||
{
|
||||
renderGuisActive = value;
|
||||
}
|
||||
|
||||
bool isGearTypeAllowed(GearType gearType);
|
||||
|
||||
static void setLoaderFunction(boost::function<void(Aya::DataModel*)> loader)
|
||||
{
|
||||
loaderFunc = loader;
|
||||
}
|
||||
|
||||
void loadPlugins();
|
||||
|
||||
struct MouseStats
|
||||
{
|
||||
RunningAverageTimeInterval<> osMouseMove;
|
||||
RunningAverageTimeInterval<> mouseMove;
|
||||
|
||||
MouseStats()
|
||||
: osMouseMove(0.5)
|
||||
, mouseMove(0.5)
|
||||
{
|
||||
}
|
||||
};
|
||||
MouseStats mouseStats;
|
||||
|
||||
bool getSharedSuppressNavKeys() const
|
||||
{
|
||||
return (game ? game->getSuppressNavKeys() : suppressNavKeys);
|
||||
}
|
||||
void setGame(Aya::Game* newGame)
|
||||
{
|
||||
game = newGame;
|
||||
}
|
||||
|
||||
bool getIsShuttingDown()
|
||||
{
|
||||
return isShuttingDown;
|
||||
}
|
||||
void setIsShuttingDown(bool value)
|
||||
{
|
||||
isShuttingDown = value;
|
||||
}
|
||||
|
||||
private:
|
||||
typedef DescribedNonCreatable<DataModel, ServiceProvider, sDataModel> Super;
|
||||
|
||||
ThrottlingHelper savePlaceThrottle;
|
||||
|
||||
// TODO: turn some of these into non-essential Services? Or at least, see if we can
|
||||
// get rid of the direct references here...
|
||||
|
||||
shared_ptr<CoreGuiService> coreGuiService;
|
||||
shared_ptr<ContextActionService> contextActionService;
|
||||
|
||||
shared_ptr<StarterPackService> starterPackService;
|
||||
shared_ptr<StarterGuiService> starterGuiService;
|
||||
shared_ptr<StarterPlayerService> starterPlayerService;
|
||||
shared_ptr<RunService> runService;
|
||||
shared_ptr<UserInputService> userInputService;
|
||||
shared_ptr<Workspace> workspace;
|
||||
|
||||
shared_ptr<GuiRoot> guiRoot;
|
||||
|
||||
bool forceArrowCursor;
|
||||
virtual GuiResponse processAccelerators(const shared_ptr<InputObject>& event);
|
||||
virtual GuiResponse processCameraCommands(const shared_ptr<InputObject>& event);
|
||||
|
||||
std::string uiMessage; // short term hack - will improve
|
||||
int drawId; // flag to debug extra draws
|
||||
IMetric* tempMetric; // for output in ctrl-F1 message - only used during render call
|
||||
IMetric* networkMetric; // for output in ctrl-F1 message - only used during render call
|
||||
|
||||
volatile bool dirty; // For IDataState
|
||||
bool isInitialized; // Block gui events when closing - per crash reports on 12/05/07
|
||||
bool isContentLoaded;
|
||||
volatile bool isShuttingDown;
|
||||
|
||||
boost::mutex hackFlagSetMutex;
|
||||
boost::unordered_set<unsigned int> hackFlagSet;
|
||||
|
||||
PhysicsInstructions physicsInstructions;
|
||||
|
||||
int numPartInstances;
|
||||
int numInstances;
|
||||
int shutdownRequestedCount;
|
||||
int physicsStepID;
|
||||
int totalWorldSteps;
|
||||
bool isGameLoaded;
|
||||
|
||||
bool forceR15;
|
||||
bool canRequestUniverseInfo;
|
||||
bool universeDataRequested;
|
||||
void requestGameStartInfo();
|
||||
|
||||
bool isPersonalServer;
|
||||
|
||||
bool networkStatsWindowsOn;
|
||||
|
||||
bool renderGuisActive;
|
||||
|
||||
Aya::Game* game;
|
||||
|
||||
Time dataModelInitTime;
|
||||
|
||||
TextureProxyBaseRef renderCursor;
|
||||
|
||||
void initializeContents(bool startHeartbeat);
|
||||
|
||||
void doDataModelStep(float timeInterval);
|
||||
bool updatePhysicsInstructions(Network::GameMode gameMode);
|
||||
bool onlyJobsLeftForThisArbiterAreGenericJobs();
|
||||
|
||||
static boost::function<void(Aya::DataModel*)> loaderFunc;
|
||||
|
||||
bool areCoreScriptsLoaded;
|
||||
|
||||
std::auto_ptr<std::istream> loadAssetIdIntoStream(int assetID);
|
||||
|
||||
public:
|
||||
static bool BlockingDataModelShutdown;
|
||||
|
||||
static unsigned int perfStats; // another bitmask used to record detected hacks
|
||||
|
||||
std::string jobId;
|
||||
|
||||
static unsigned int sendStats; // legacy bitmask used to record detected hacks
|
||||
|
||||
std::string getJobId() const
|
||||
{
|
||||
return jobId;
|
||||
};
|
||||
|
||||
Time getDataModelInitTime() const
|
||||
{
|
||||
return dataModelInitTime;
|
||||
}
|
||||
|
||||
static std::string hash; // set to the hash of the client exe
|
||||
|
||||
//-----------------------------------
|
||||
// Concurrency violation detection
|
||||
// TODO: Merge with readwrite_concurrency_catcher class
|
||||
class scoped_write_request
|
||||
{
|
||||
DataModel* const dataModel;
|
||||
|
||||
public:
|
||||
// Place this code around tasks that write to a DataModel
|
||||
scoped_write_request(Instance* context);
|
||||
~scoped_write_request();
|
||||
};
|
||||
class scoped_read_request
|
||||
{
|
||||
DataModel* const dataModel;
|
||||
|
||||
public:
|
||||
// Place this code around tasks that read a DataModel
|
||||
scoped_read_request(Instance* context);
|
||||
~scoped_read_request();
|
||||
};
|
||||
class scoped_write_transfer
|
||||
{
|
||||
DataModel* const dataModel;
|
||||
unsigned int oldWritingThread;
|
||||
|
||||
public:
|
||||
// Place this code around tasks that write to a DataModel and expect some other task to hold the write lock for them
|
||||
scoped_write_transfer(Instance* context);
|
||||
~scoped_write_transfer();
|
||||
};
|
||||
friend class scoped_write_request;
|
||||
friend class scoped_read_request;
|
||||
volatile long write_requested;
|
||||
volatile long read_requested;
|
||||
volatile DWORD writeRequestingThread;
|
||||
|
||||
bool currentThreadHasWriteLock() const;
|
||||
static bool currentThreadHasWriteLock(Instance* context);
|
||||
|
||||
void setNetworkStatsWindowsOn(bool val)
|
||||
{
|
||||
networkStatsWindowsOn = val;
|
||||
}
|
||||
|
||||
friend class Game;
|
||||
|
||||
//
|
||||
//-----------------------------------
|
||||
|
||||
// It is important for addHackFlag to be inlined for security reasons.
|
||||
// If this function is not inlined, it provides a single point of attack.
|
||||
// Also inlining the code (along with VMProtect) provides more obscurity.
|
||||
#ifdef WIN32
|
||||
__forceinline
|
||||
#else
|
||||
inline
|
||||
#endif
|
||||
void
|
||||
addHackFlag(unsigned int flag)
|
||||
{
|
||||
boost::mutex::scoped_lock l(hackFlagSetMutex);
|
||||
hackFlagSet.insert(flag);
|
||||
sendStats |= flag;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
__forceinline
|
||||
#else
|
||||
inline
|
||||
#endif
|
||||
void
|
||||
removeHackFlag(unsigned int flag)
|
||||
{
|
||||
boost::mutex::scoped_lock l(hackFlagSetMutex);
|
||||
hackFlagSet.erase(flag);
|
||||
sendStats &= ~flag;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
__forceinline
|
||||
#else
|
||||
inline
|
||||
#endif
|
||||
bool
|
||||
isHackFlagSet(unsigned int flag)
|
||||
{
|
||||
boost::mutex::scoped_lock l(hackFlagSetMutex);
|
||||
return hackFlagSet.find(flag) != hackFlagSet.end() || (sendStats & flag);
|
||||
}
|
||||
|
||||
unsigned int allHackFlagsOredTogether();
|
||||
|
||||
weak_ptr<GuiObject> getMouseOverInteractable() const
|
||||
{
|
||||
return mouseOverInteractable;
|
||||
}
|
||||
bool getMouseOverGui() const
|
||||
{
|
||||
return mouseOverGui;
|
||||
}
|
||||
void processWorkspaceEvent(const shared_ptr<InputObject>& event);
|
||||
|
||||
bool isClosed() const
|
||||
{
|
||||
return !isInitialized;
|
||||
}
|
||||
void setSuppressNavKeys(bool value)
|
||||
{
|
||||
suppressNavKeys = value;
|
||||
}
|
||||
|
||||
static bool throttleAt30Fps; // for debugging/benchmarking - default is false;
|
||||
|
||||
// This event is fired anytime anything in the DataModel changes
|
||||
Aya::signal<void(shared_ptr<Instance>, const Reflection::PropertyDescriptor*)> itemChangedSignal;
|
||||
|
||||
void clearContents(bool resettingSimulation);
|
||||
|
||||
void setInitialScreenSize(Aya::Vector2 newScreenSize);
|
||||
|
||||
// submits a task to be executed in a thread-safe manner
|
||||
typedef boost::function<void(DataModel*)> Task;
|
||||
void submitTask(Task task, DataModelJob::TaskType taskType);
|
||||
shared_ptr<const Reflection::ValueArray> getJobsInfo();
|
||||
|
||||
void setCreatorID(int creatorID, CreatorType creatorType);
|
||||
int getCreatorID() const
|
||||
{
|
||||
return creatorID;
|
||||
}
|
||||
CreatorType getCreatorType() const
|
||||
{
|
||||
return creatorType;
|
||||
}
|
||||
|
||||
int getPlaceID() const
|
||||
{
|
||||
return placeID;
|
||||
}
|
||||
int getPlaceIDOrZeroInStudio();
|
||||
void setPlaceID(int placeID, bool robloxPlace);
|
||||
Aya::GameBasicSettings::VirtualVersion getInitialVersion() const
|
||||
{
|
||||
return initialVirtualVersion;
|
||||
}
|
||||
void setInitialVersion(Aya::GameBasicSettings::VirtualVersion vv)
|
||||
{
|
||||
this->initialVirtualVersion = vv;
|
||||
}
|
||||
const;
|
||||
|
||||
bool showLoadingScreen() const
|
||||
{
|
||||
return shouldShowLoadingScreen;
|
||||
}
|
||||
void setShouldShowLoadingScreen(bool show)
|
||||
{
|
||||
this->shouldShowLoadingScreen = show;
|
||||
}
|
||||
const;
|
||||
|
||||
|
||||
void setGameInstanceID(std::string gameInstanceID)
|
||||
{
|
||||
this->gameInstanceID = gameInstanceID;
|
||||
}
|
||||
std::string getGameInstanceID()
|
||||
{
|
||||
return this->gameInstanceID;
|
||||
}
|
||||
|
||||
int getUniverseId()
|
||||
{
|
||||
return universeId;
|
||||
}
|
||||
void setUniverseId(int uId);
|
||||
|
||||
bool isStudio() const
|
||||
{
|
||||
return runningInStudio;
|
||||
}
|
||||
void setIsStudio(bool runningInStudio);
|
||||
|
||||
bool isRunMode() const
|
||||
{
|
||||
return isStudioRunMode;
|
||||
}
|
||||
void setIsRunMode(bool value);
|
||||
|
||||
int getPlaceVersion() const
|
||||
{
|
||||
return placeVersion;
|
||||
}
|
||||
void setPlaceVersion(int placeVersion);
|
||||
|
||||
bool getIsPersonalServer() const
|
||||
{
|
||||
return isPersonalServer;
|
||||
}
|
||||
void setIsPersonalServer(bool value)
|
||||
{
|
||||
isPersonalServer = value;
|
||||
}
|
||||
|
||||
bool getIsGameLoaded()
|
||||
{
|
||||
return isGameLoaded;
|
||||
}
|
||||
void setIsGameLoaded(bool value)
|
||||
{
|
||||
isGameLoaded = value;
|
||||
if (isGameLoaded)
|
||||
gameLoadedSignal();
|
||||
}
|
||||
void gameLoaded();
|
||||
|
||||
bool getForceR15() const
|
||||
{
|
||||
return forceR15;
|
||||
}
|
||||
void setForceR15(bool v);
|
||||
boost::promise<void> universeDataLoaded;
|
||||
bool getUniverseDataRequested() const
|
||||
{
|
||||
return universeDataRequested;
|
||||
}
|
||||
void clearUniverseDataRequested()
|
||||
{
|
||||
universeDataRequested = false;
|
||||
}
|
||||
void setCanRequestUniverseInfo(bool value);
|
||||
|
||||
void loadCoreScripts(const std::string& altStarterScript = "");
|
||||
|
||||
|
||||
int getNumPartInstances() const
|
||||
{
|
||||
return numPartInstances;
|
||||
}
|
||||
int getNumInstance() const
|
||||
{
|
||||
return numInstances;
|
||||
}
|
||||
|
||||
void checkFetchExperimentalFeatures();
|
||||
|
||||
bool canSaveLocal() const;
|
||||
void saveToRoblox(boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
void completeShutdown(bool saveLocal);
|
||||
boost::function<bool()> requestShutdownCallback;
|
||||
|
||||
typedef boost::function<void(Reflection::AsyncCallbackDescriptor::ResumeFunction, Reflection::AsyncCallbackDescriptor::ErrorFunction)>
|
||||
CloseCallback;
|
||||
CloseCallback onCloseCallback;
|
||||
void onCloseCallbackChanged(const CloseCallback&);
|
||||
|
||||
// Returns true if the shutdown is being handled by Lua, false to use the System Level Prompts
|
||||
RequestShutdownResult requestShutdown(bool useLuaShutdownForSave = true);
|
||||
|
||||
// for perf stats reporting:
|
||||
// width of window to measure. Set to zero to turn off and release memory.
|
||||
void setJobsExtendedStatsWindow(double seconds);
|
||||
shared_ptr<const Reflection::ValueArray> getJobsExtendedStats();
|
||||
double getJobTimePeakFraction(std::string jobname, double greaterThan);
|
||||
double getJobIntervalPeakFraction(std::string jobname, double greaterThan);
|
||||
|
||||
class GenericJob;
|
||||
|
||||
class LegacyLock : boost::noncopyable
|
||||
{
|
||||
struct Implementation;
|
||||
boost::scoped_ptr<Implementation> const implementation;
|
||||
|
||||
public:
|
||||
static int mainThreadId;
|
||||
|
||||
class Impersonator : boost::noncopyable
|
||||
{
|
||||
// Use this class to override a Lock. This must only be used
|
||||
// when you know that a different thread is delegating the lock
|
||||
// to you.
|
||||
// TODO: For added safety we could write a stack-based Delegator
|
||||
// object that would be constructed at the source thread. The
|
||||
// Delegator would issue a token to be used by the thread that
|
||||
// uses the Impersonator
|
||||
public:
|
||||
Impersonator(shared_ptr<DataModel> dataModel, DataModelJob::TaskType taskType);
|
||||
~Impersonator();
|
||||
|
||||
private:
|
||||
DataModel::GenericJob* previousJob;
|
||||
};
|
||||
LegacyLock(shared_ptr<DataModel> dataModel, DataModelJob::TaskType taskType);
|
||||
LegacyLock(DataModel* dataModel, DataModelJob::TaskType taskType);
|
||||
~LegacyLock();
|
||||
|
||||
static bool hasLegacyLock(DataModel* dataModel);
|
||||
};
|
||||
|
||||
// Please call these to construct and release a datamodel
|
||||
static shared_ptr<DataModel> createDataModel(bool startHeartbeat, Aya::Verb* lockVerb, bool shouldShowLoadingScreen,
|
||||
GameBasicSettings::VirtualVersion vv = GameBasicSettings::VERSION_2016);
|
||||
|
||||
static void closeDataModel(shared_ptr<DataModel> dataModel);
|
||||
|
||||
~DataModel();
|
||||
DataModel(Aya::Verb* lockVerb);
|
||||
static DataModel* get(Instance* context);
|
||||
static const DataModel* get(const Instance* context);
|
||||
|
||||
void loadGame(int assetID);
|
||||
void loadWorld(int assetID);
|
||||
|
||||
void loadContent(ContentId contentId);
|
||||
void processAfterLoad();
|
||||
Aya::signal<void()> saveFinishedSignal;
|
||||
|
||||
|
||||
class SerializationException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
SerializationException(const std::string& response)
|
||||
: std::runtime_error(response)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
void save(ContentId contentId);
|
||||
static bool canSave(const Aya::Instance* instance);
|
||||
|
||||
bool getRemoteBuildMode();
|
||||
void setRemoteBuildMode(bool remoteBuildMode);
|
||||
|
||||
void setServerSaveUrl(std::string url);
|
||||
void serverSave();
|
||||
|
||||
bool serverSavePlace(
|
||||
const SaveFilter saveFilter, boost::function<void(bool)> resumeFunction = NULL, boost::function<void(std::string)> errorFunction = NULL);
|
||||
shared_ptr<std::stringstream> serializeDataModel(const Instance::SaveFilter saveFilter = Instance::SAVE_ALL);
|
||||
|
||||
virtual void savePlaceAsync(
|
||||
const SaveFilter saveFilter, boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
|
||||
void httpGetAsync(std::string url, boost::function<void(std::string)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
void httpPostAsync(std::string url, std::string data, std::string optionalContentType, boost::function<void(std::string)> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction);
|
||||
|
||||
std::string httpGet(std::string url, bool synchronous);
|
||||
std::string httpPost(std::string url, std::string data, bool synchronous, std::string optionalContentType);
|
||||
|
||||
void reportMeasurement(std::string id, std::string key1, std::string value1, std::string key2, std::string value2);
|
||||
|
||||
void close();
|
||||
|
||||
shared_ptr<const Instances> fetchAsset(ContentId contentId);
|
||||
|
||||
void raiseClose();
|
||||
|
||||
static void ShowMessage(weak_ptr<Aya::DataModel> weakDataModel, int slot, const std::string& message, double duration);
|
||||
|
||||
void addCustomStat(std::string name, std::string value);
|
||||
void removeCustomStat(std::string str);
|
||||
void writeStatsSettings();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Command Processing
|
||||
int numChatOptions()
|
||||
{
|
||||
return 4;
|
||||
} // To Do - make this some kind of dynamic loading
|
||||
|
||||
void startCoreScripts(bool buildInGameGui, const std::string& altStarterScript = "");
|
||||
|
||||
void setGuiTargetInstance(shared_ptr<Instance> newTargetInstance)
|
||||
{
|
||||
guiTargetInstance = newTargetInstance;
|
||||
}
|
||||
|
||||
bool processInputObject(shared_ptr<InputObject> event);
|
||||
GuiRoot* getGuiRoot()
|
||||
{
|
||||
return guiRoot.get();
|
||||
}
|
||||
|
||||
void setForceArrowCursor(bool value)
|
||||
{
|
||||
forceArrowCursor = value;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// IDataState
|
||||
virtual void setDirty(bool dirty)
|
||||
{
|
||||
this->dirty = dirty;
|
||||
}
|
||||
|
||||
// Thread-safe:
|
||||
virtual bool isDirty() const
|
||||
{
|
||||
return dirty;
|
||||
}
|
||||
|
||||
virtual std::string arbiterName()
|
||||
{
|
||||
return this->getName();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Workspace Stuff / Running Stuff
|
||||
//
|
||||
Workspace* getWorkspace() const
|
||||
{
|
||||
return workspace.get();
|
||||
}
|
||||
|
||||
float physicsStep(float timeInterval, double dt, double dutyDt, int numThreads);
|
||||
void renderStep(float timeIntervalSeconds);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DataModelArbiter
|
||||
//
|
||||
/*implement*/ int getNumPlayers() const;
|
||||
/*implement*/ int getMaxPlayers() const;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// timing state
|
||||
//
|
||||
double getGameTime() const; // i.e. game time
|
||||
double getSmoothFps() const;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMetric
|
||||
//
|
||||
//
|
||||
/*override*/ virtual std::string getMetric(const std::string& metric) const;
|
||||
/*override*/ virtual double getMetricValue(const std::string& metric) const;
|
||||
|
||||
std::string getUpdatedMessageBoxText();
|
||||
void setNetworkMetric(IMetric* metric);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Rendering
|
||||
//
|
||||
virtual void renderPass2d(Adorn* adorn, IMetric* graphicsMetric);
|
||||
void renderPass3dAdorn(Adorn* adorn);
|
||||
void setUiMessage(std::string message);
|
||||
void clearUiMessage();
|
||||
void setUiMessageBrickCount();
|
||||
void toggleToolsOff();
|
||||
|
||||
virtual void renderMouse(Adorn* adorn);
|
||||
ContentId getRenderMouseCursor();
|
||||
|
||||
void computeGuiInset(Adorn* adorn);
|
||||
void renderPlayerGui(Adorn* adorn);
|
||||
void renderDebugMenu();
|
||||
void renderMessageBox(Adorn* adorn);
|
||||
static void HttpHelper(std::string* response, std::exception* exception, boost::function<void(std::string)> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction);
|
||||
|
||||
DebugMenu& getDebugMenu()
|
||||
{
|
||||
return *debugMenu;
|
||||
}
|
||||
|
||||
TaskScheduler::Arbiter* getSyncronizationArbiter();
|
||||
|
||||
bool uploadPlace(const std::string& uploadUrl, const SaveFilter saveFilter, boost::function<void()> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction);
|
||||
|
||||
GuiResponse processPlayerGui(const shared_ptr<InputObject>& event);
|
||||
|
||||
static void processHttpRequestResponseOnLock(DataModel* dataModel, std::string* response, std::exception* exception,
|
||||
boost::function<void(shared_ptr<std::string>, shared_ptr<std::exception> exception)> onLockAcquired);
|
||||
|
||||
protected:
|
||||
static void doDataModelSetup(shared_ptr<DataModel> dataModel, bool startHeartbeat, bool shouldShowLoadingScreen);
|
||||
|
||||
void internalSave(ContentId contentId);
|
||||
void internalSaveAsync(ContentId contentId, boost::function<void(bool)> resumeFunction);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Instance overrides
|
||||
/*override*/ bool askAddChild(const Instance* instance) const;
|
||||
/*override*/ void onChildAdded(Instance* child);
|
||||
|
||||
/*override*/ void onChildChanged(Instance* instance, const PropertyChanged& event);
|
||||
/*override*/ void onDescendantAdded(Instance* instance);
|
||||
/*override*/ void onDescendantRemoving(const shared_ptr<Instance>& instance);
|
||||
void onRunTransition(RunTransition event);
|
||||
|
||||
bool processEvent(const shared_ptr<InputObject>& event);
|
||||
GuiResponse processProfilerEvent(const shared_ptr<InputObject>& event);
|
||||
|
||||
weak_ptr<Instance> guiTargetInstance;
|
||||
bool mouseOverGui;
|
||||
weak_ptr<GuiObject> mouseOverInteractable;
|
||||
|
||||
bool getSuppressNavKeys() const
|
||||
{
|
||||
return suppressNavKeys;
|
||||
}
|
||||
|
||||
private:
|
||||
Aya::signals::scoped_connection unbindResourceSignal;
|
||||
void onUnbindResourceSignal();
|
||||
|
||||
bool suppressNavKeys;
|
||||
static std::string doHttpGet(const std::string& url);
|
||||
static void doHttpGet(
|
||||
const std::string& url, boost::function<void(std::string)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
static std::string doHttpPost(const std::string& url, const std::string& data, const std::string& contentType);
|
||||
static void doHttpPost(const std::string& url, const std::string& data, const std::string& contentType,
|
||||
boost::function<void(std::string)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
|
||||
|
||||
static void doCloseDataModel(shared_ptr<DataModel> dataModel);
|
||||
|
||||
void AsyncUploadPlaceResponseHandler(
|
||||
std::string* response, std::exception* exception, boost::function<void()> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
bool uploadPlaceReturn(
|
||||
const bool succeeded, const std::string error, boost::function<void()> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
|
||||
bool canRenderMouse();
|
||||
|
||||
GuiResponse processDevGamepadEvent(const shared_ptr<InputObject>& event);
|
||||
GuiResponse processCoreGamepadEvent(const shared_ptr<InputObject>& event);
|
||||
GuiResponse processGuiTarget(const shared_ptr<InputObject>& event);
|
||||
|
||||
typedef boost::array<shared_ptr<GenericJob>, DataModelJob::TaskTypeMax> GenericJobs;
|
||||
Aya::mutex genericJobsLock;
|
||||
GenericJobs genericJobs;
|
||||
Aya::mutex debugLock;
|
||||
shared_ptr<GenericJob> tryGetGenericJob(DataModelJob::TaskType type);
|
||||
shared_ptr<GenericJob> getGenericJob(DataModelJob::TaskType type);
|
||||
|
||||
std::map<std::string, int> dataModelReportingData;
|
||||
void traverseDataModelReporting(shared_ptr<Instance> child);
|
||||
|
||||
Instance* getLightingDeprecated() const;
|
||||
static Reflection::RefPropDescriptor<DataModel, Instance> prop_lighting;
|
||||
|
||||
bool remoteBuildMode;
|
||||
std::string serverSaveUrl;
|
||||
|
||||
HeapValue<int> placeID;
|
||||
std::string gameInstanceID;
|
||||
int universeId;
|
||||
bool runningInStudio;
|
||||
bool isStudioRunMode;
|
||||
bool checkedExperimentalFeatures;
|
||||
|
||||
int placeVersion;
|
||||
|
||||
HeapValue<int> creatorID;
|
||||
CreatorType creatorType;
|
||||
|
||||
Genre genre;
|
||||
GearGenreSetting gearGenreSetting;
|
||||
unsigned allowedGearTypes;
|
||||
|
||||
std::string vipServerId;
|
||||
int vipServerOwnerId;
|
||||
|
||||
DebugMenu* debugMenu;
|
||||
|
||||
static void buildNetworkStatsOutput(shared_ptr<Instance> instance, std::string* output);
|
||||
static void buildSimpleStatsOutput(shared_ptr<Instance> instance, std::string* output);
|
||||
};
|
||||
|
||||
} // namespace Aya
|
||||
187
engine/app/src/DataModel/DataModelJob.cpp
Normal file
187
engine/app/src/DataModel/DataModelJob.cpp
Normal file
@@ -0,0 +1,187 @@
|
||||
|
||||
|
||||
|
||||
#include "DataModel/DataModelJob.hpp"
|
||||
#include "Debug.hpp"
|
||||
#include "boost/cast.hpp"
|
||||
#include "Reflection/EnumConverter.hpp"
|
||||
#include "Utility/Object.hpp"
|
||||
|
||||
#include "Profiler.hpp"
|
||||
|
||||
using namespace Aya;
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Reflection
|
||||
{
|
||||
template<>
|
||||
Reflection::EnumDesc<DataModelArbiter::ConcurrencyModel>::EnumDesc()
|
||||
: Reflection::EnumDescriptor("ConcurrencyModel")
|
||||
{
|
||||
addPair(DataModelArbiter::Serial, "Serial");
|
||||
addPair(DataModelArbiter::Safe, "Safe");
|
||||
addPair(DataModelArbiter::Logical, "Logical");
|
||||
addPair(DataModelArbiter::Empirical, "Empirical");
|
||||
}
|
||||
} // namespace Reflection
|
||||
} // namespace Aya
|
||||
|
||||
DataModelJob::DataModelJob(const char* name, TaskType taskType, bool isPerPlayer, shared_ptr<DataModelArbiter> arbiter, Time::Interval stepBudget)
|
||||
: Job(name, arbiter, stepBudget)
|
||||
, taskType(taskType)
|
||||
, stepsAccumulated(0)
|
||||
, isPerPlayer(isPerPlayer)
|
||||
, profilingToken(0)
|
||||
{
|
||||
AYAASSERT(arbiter);
|
||||
|
||||
#ifdef AYAPROFILER
|
||||
profilingToken = Profiler::getToken("Jobs", name);
|
||||
#endif
|
||||
}
|
||||
|
||||
TaskScheduler::StepResult DataModelJob::step(const Stats& stats)
|
||||
{
|
||||
#ifdef AYAPROFILER
|
||||
Profiler::Scope profilingScope(profilingToken);
|
||||
#endif
|
||||
|
||||
return stepDataModelJob(stats);
|
||||
}
|
||||
|
||||
double DataModelJob::updateStepsRequiredForCyclicExecutive(float stepDt, float desiredHz, float maxStepsPerCycle, float maxStepsAccumulated)
|
||||
{
|
||||
float desiredStepsFloat = stepDt * desiredHz + stepsAccumulated;
|
||||
|
||||
float desiredStepsFloor = ::floorf(desiredStepsFloat);
|
||||
desiredStepsFloor = std::min(desiredStepsFloor, maxStepsPerCycle);
|
||||
stepsAccumulated = desiredStepsFloat - desiredStepsFloor;
|
||||
stepsAccumulated = std::min((float)stepsAccumulated, maxStepsAccumulated);
|
||||
|
||||
if (desiredStepsFloor <= 0.0f)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
return desiredStepsFloor;
|
||||
}
|
||||
|
||||
double DataModelJob::getPriorityFactor()
|
||||
{
|
||||
// shared_ptr<DataModelArbiter> arbiter(shared_polymorphic_downcast<DataModelArbiter>(getArbiter()));
|
||||
// if (!arbiter)
|
||||
// return 1;
|
||||
|
||||
return isPerPlayer ? 1 : std::max(1, getArbiter()->getNumPlayers());
|
||||
}
|
||||
|
||||
// The default is "Logical"
|
||||
DataModelArbiter::ConcurrencyModel DataModelArbiter::concurrencyModel = Logical;
|
||||
|
||||
bool DataModelArbiter::areExclusive(TaskScheduler::Job* job1, TaskScheduler::Job* job2)
|
||||
{
|
||||
DataModelJob* j1 = boost::polymorphic_downcast<DataModelJob*>(job1);
|
||||
DataModelJob* j2 = boost::polymorphic_downcast<DataModelJob*>(job2);
|
||||
return areExclusive(j1->taskType, j2->taskType);
|
||||
}
|
||||
|
||||
|
||||
bool DataModelArbiter::areExclusive(DataModelJob::TaskType function1, DataModelJob::TaskType function2)
|
||||
{
|
||||
return lookup[concurrencyModel][function1][function2];
|
||||
}
|
||||
|
||||
void static copy(
|
||||
bool src[DataModelJob::TaskTypeCount][DataModelJob::TaskTypeCount], bool dst[DataModelJob::TaskTypeCount][DataModelJob::TaskTypeCount])
|
||||
{
|
||||
for (int j = 0; j < DataModelJob::TaskTypeCount; ++j)
|
||||
for (int k = 0; k < DataModelJob::TaskTypeCount; ++k)
|
||||
dst[j][k] = src[j][k];
|
||||
}
|
||||
|
||||
static void setConcurrent(bool table[DataModelJob::TaskTypeCount][DataModelJob::TaskTypeCount], DataModelJob::TaskType f1, DataModelJob::TaskType f2)
|
||||
{
|
||||
table[f1][f2] = false;
|
||||
table[f2][f1] = false;
|
||||
}
|
||||
|
||||
DataModelArbiter::DataModelArbiter(void)
|
||||
{
|
||||
for (int j = 0; j < DataModelJob::TaskTypeCount; ++j)
|
||||
for (int k = 0; k < DataModelJob::TaskTypeCount; ++k)
|
||||
lookup[Serial][j][k] = true; // default all to exclusive
|
||||
|
||||
{
|
||||
copy(lookup[Serial], lookup[Safe]);
|
||||
// Open up concurrency for Safe:
|
||||
|
||||
setConcurrent(lookup[Safe], DataModelJob::PhysicsOut, DataModelJob::PhysicsOut);
|
||||
setConcurrent(lookup[Safe], DataModelJob::DataOut, DataModelJob::DataOut);
|
||||
setConcurrent(lookup[Safe], DataModelJob::None, DataModelJob::None);
|
||||
|
||||
// RaknetPeer can be concurrent with everything except for Write,Physics,RaknetPeer:
|
||||
setConcurrent(lookup[Safe], DataModelJob::RaknetPeer, DataModelJob::PhysicsOut);
|
||||
setConcurrent(lookup[Safe], DataModelJob::RaknetPeer, DataModelJob::DataOut);
|
||||
setConcurrent(lookup[Safe], DataModelJob::RaknetPeer, DataModelJob::PhysicsIn);
|
||||
setConcurrent(lookup[Safe], DataModelJob::RaknetPeer, DataModelJob::DataIn);
|
||||
setConcurrent(lookup[Safe], DataModelJob::RaknetPeer, DataModelJob::Read);
|
||||
setConcurrent(lookup[Safe], DataModelJob::RaknetPeer, DataModelJob::Render);
|
||||
setConcurrent(lookup[Safe], DataModelJob::RaknetPeer, DataModelJob::None);
|
||||
|
||||
// PhysicsOutSort can be concurrent with everything except PhysicsOut
|
||||
setConcurrent(lookup[Safe], DataModelJob::PhysicsOutSort, DataModelJob::DataOut);
|
||||
setConcurrent(lookup[Safe], DataModelJob::PhysicsOutSort, DataModelJob::PhysicsIn);
|
||||
setConcurrent(lookup[Safe], DataModelJob::PhysicsOutSort, DataModelJob::DataIn);
|
||||
setConcurrent(lookup[Safe], DataModelJob::PhysicsOutSort, DataModelJob::Read);
|
||||
setConcurrent(lookup[Safe], DataModelJob::PhysicsOutSort, DataModelJob::Render);
|
||||
setConcurrent(lookup[Safe], DataModelJob::PhysicsOutSort, DataModelJob::None);
|
||||
|
||||
setConcurrent(lookup[Safe], DataModelJob::None, DataModelJob::PhysicsOut);
|
||||
setConcurrent(lookup[Safe], DataModelJob::None, DataModelJob::DataOut);
|
||||
setConcurrent(lookup[Safe], DataModelJob::None, DataModelJob::PhysicsIn);
|
||||
setConcurrent(lookup[Safe], DataModelJob::None, DataModelJob::DataIn);
|
||||
setConcurrent(lookup[Safe], DataModelJob::None, DataModelJob::Read);
|
||||
setConcurrent(lookup[Safe], DataModelJob::None, DataModelJob::Write);
|
||||
setConcurrent(lookup[Safe], DataModelJob::None, DataModelJob::Render);
|
||||
setConcurrent(lookup[Safe], DataModelJob::None, DataModelJob::Physics);
|
||||
setConcurrent(lookup[Safe], DataModelJob::None, DataModelJob::RaknetPeer);
|
||||
}
|
||||
|
||||
{
|
||||
copy(lookup[Safe], lookup[Logical]);
|
||||
|
||||
// Open up concurrency for Logical:
|
||||
setConcurrent(lookup[Logical], DataModelJob::DataOut, DataModelJob::PhysicsOut);
|
||||
|
||||
// Can't do rendering in parallel because it currently makes changes to the DataModel
|
||||
// setConcurrent(lookup[Logical], DataModelJob::Render, DataModelJob::PhysicsOut);
|
||||
|
||||
setConcurrent(lookup[Logical], DataModelJob::Read, DataModelJob::Read);
|
||||
setConcurrent(lookup[Logical], DataModelJob::Read, DataModelJob::DataOut);
|
||||
setConcurrent(lookup[Logical], DataModelJob::Read, DataModelJob::PhysicsOut);
|
||||
|
||||
// Note: Render and Read are not allowed, because Render can modify the Camera
|
||||
// Note: Render and DataOut are not allowed, because Render can modify the Camera
|
||||
}
|
||||
|
||||
{
|
||||
copy(lookup[Logical], lookup[Empirical]);
|
||||
|
||||
// Open up concurrency for Imperical:
|
||||
// setConcurrent(lookup[Empirical], DataModelJob::Render, DataModelJob::PhysicsIn);
|
||||
// setConcurrent(lookup[Empirical], DataModelJob::Render, DataModelJob::Physics);
|
||||
}
|
||||
}
|
||||
|
||||
// This does not get called closing a place in Studio.
|
||||
DataModelArbiter::~DataModelArbiter() {}
|
||||
|
||||
void DataModelArbiter::preStep(TaskScheduler::Job* job)
|
||||
{
|
||||
activityMeter.increment();
|
||||
}
|
||||
|
||||
void DataModelArbiter::postStep(TaskScheduler::Job* job)
|
||||
{
|
||||
activityMeter.decrement();
|
||||
}
|
||||
73
engine/app/src/DataModel/DataModelJob.hpp
Normal file
73
engine/app/src/DataModel/DataModelJob.hpp
Normal file
@@ -0,0 +1,73 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TaskScheduler.Job.hpp"
|
||||
|
||||
LOGGROUP(DataModelJobs)
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
class DataModelArbiter;
|
||||
|
||||
class DataModelJob : public TaskScheduler::Job
|
||||
{
|
||||
public:
|
||||
typedef enum
|
||||
{
|
||||
Read = 0, // general read-only access to the DataModel
|
||||
Write, // general read-writes access to the DataModel
|
||||
Render,
|
||||
Physics,
|
||||
DataOut,
|
||||
PhysicsOut,
|
||||
PhysicsOutSort,
|
||||
DataIn,
|
||||
PhysicsIn,
|
||||
RaknetPeer,
|
||||
None,
|
||||
TaskTypeMax
|
||||
} TaskType;
|
||||
static const int TaskTypeCount = TaskTypeMax;
|
||||
TaskType taskType;
|
||||
|
||||
virtual TaskScheduler::StepResult step(const Stats& stats);
|
||||
|
||||
protected:
|
||||
virtual TaskScheduler::StepResult stepDataModelJob(const Stats& stats) = 0;
|
||||
virtual double updateStepsRequiredForCyclicExecutive(float stepDt, float desiredHz, float maxStepsPerCycle, float maxStepsAccumulated);
|
||||
double stepsAccumulated;
|
||||
const bool isPerPlayer;
|
||||
unsigned long long profilingToken;
|
||||
DataModelJob(const char* name, TaskType taskType, bool isPerPlayer, shared_ptr<DataModelArbiter> arbiter, Time::Interval stepBudget);
|
||||
/*implement*/ double getPriorityFactor();
|
||||
};
|
||||
|
||||
class DataModelArbiter : public SimpleThrottlingArbiter
|
||||
{
|
||||
public:
|
||||
typedef enum
|
||||
{
|
||||
Serial = 0,
|
||||
Safe,
|
||||
Logical,
|
||||
Empirical
|
||||
} ConcurrencyModel;
|
||||
static const int ConcurrencyModelCount = 4;
|
||||
static ConcurrencyModel concurrencyModel;
|
||||
DataModelArbiter();
|
||||
virtual ~DataModelArbiter();
|
||||
virtual bool areExclusive(TaskScheduler::Job* job1, TaskScheduler::Job* job2);
|
||||
virtual int getNumPlayers() const = 0;
|
||||
bool areExclusive(DataModelJob::TaskType task1, DataModelJob::TaskType task2);
|
||||
|
||||
virtual void preStep(TaskScheduler::Job* job);
|
||||
virtual void postStep(TaskScheduler::Job* job);
|
||||
|
||||
private:
|
||||
bool lookup[ConcurrencyModelCount][DataModelJob::TaskTypeCount][DataModelJob::TaskTypeCount];
|
||||
};
|
||||
|
||||
|
||||
} // namespace Aya
|
||||
122
engine/app/src/DataModel/DataModelMesh.cpp
Normal file
122
engine/app/src/DataModel/DataModelMesh.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
|
||||
|
||||
|
||||
#include "DataModel/DataModelMesh.hpp"
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
#include "DataModel/SpecialMesh.hpp"
|
||||
#include "DataModel/PartCookie.hpp"
|
||||
|
||||
#include "Humanoid/Humanoid.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
const char* const sDataModelMesh = "DataModelMesh";
|
||||
|
||||
namespace Reflection
|
||||
{
|
||||
template<>
|
||||
EnumDesc<DataModelMesh::LODType>::EnumDesc()
|
||||
: EnumDescriptor("LevelOfDetailSetting")
|
||||
{
|
||||
addPair(DataModelMesh::HIGH_LOD, "High");
|
||||
addPair(DataModelMesh::MEDIUM_LOD, "Medium");
|
||||
addPair(DataModelMesh::LOW_LOD, "Low");
|
||||
}
|
||||
} // namespace Reflection
|
||||
|
||||
|
||||
static Reflection::EnumPropDescriptor<DataModelMesh, DataModelMesh::LODType> desc_levelOfDetailX(
|
||||
"LODX", category_Data, &DataModelMesh::getLevelOfDetailX, &DataModelMesh::setLevelOfDetailX, Aya::Reflection::PropertyDescriptor::STREAMING);
|
||||
static Reflection::EnumPropDescriptor<DataModelMesh, DataModelMesh::LODType> desc_levelOfDetailY(
|
||||
"LODY", category_Data, &DataModelMesh::getLevelOfDetailY, &DataModelMesh::setLevelOfDetailY, Aya::Reflection::PropertyDescriptor::STREAMING);
|
||||
static Reflection::PropDescriptor<DataModelMesh, Vector3> desc_scale("Scale", category_Data, &DataModelMesh::getScale, &DataModelMesh::setScale);
|
||||
static Reflection::PropDescriptor<DataModelMesh, Vector3> desc_vertColor(
|
||||
"VertexColor", category_Data, &DataModelMesh::getVertColor, &DataModelMesh::setVertColor);
|
||||
static Reflection::PropDescriptor<DataModelMesh, Vector3> desc_offset("Offset", category_Data, &DataModelMesh::getOffset, &DataModelMesh::setOffset);
|
||||
REFLECTION_END();
|
||||
|
||||
DataModelMesh::DataModelMesh()
|
||||
: scale(1.0, 1.0, 1.0)
|
||||
, vertColor(1.0, 1.0, 1.0)
|
||||
, offset(0.0, 0.0, 0.0)
|
||||
, LODx(DataModelMesh::HIGH_LOD)
|
||||
, LODy(DataModelMesh::HIGH_LOD)
|
||||
{
|
||||
setName("Mesh");
|
||||
}
|
||||
|
||||
bool DataModelMesh::askSetParent(const Instance* instance) const
|
||||
{
|
||||
return Instance::fastDynamicCast<PartInstance>(instance) != NULL;
|
||||
}
|
||||
|
||||
|
||||
float DataModelMesh::getAlpha() const
|
||||
{
|
||||
// Alpha for a special shape is the alpha of it's parent part
|
||||
const Instance* i = this->getParent();
|
||||
if (i)
|
||||
{
|
||||
const PartInstance* pi = i->fastDynamicCast<PartInstance>();
|
||||
if (pi)
|
||||
return pi->getTransparencyUi();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void DataModelMesh::setScale(const G3D::Vector3& value)
|
||||
{
|
||||
// Sanitize the value by replacing non-finite components with 0
|
||||
// This avoids issues with tesselation while generating geometry
|
||||
G3D::Vector3 fixedValue = value;
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
if (Math::isNanInf(fixedValue[i]))
|
||||
fixedValue[i] = 0;
|
||||
|
||||
if (scale != fixedValue)
|
||||
{
|
||||
scale = fixedValue;
|
||||
raisePropertyChanged(desc_scale);
|
||||
}
|
||||
}
|
||||
|
||||
void DataModelMesh::setVertColor(const G3D::Vector3& value)
|
||||
{
|
||||
if (vertColor != value)
|
||||
{
|
||||
vertColor = value;
|
||||
raisePropertyChanged(desc_vertColor);
|
||||
}
|
||||
}
|
||||
|
||||
void DataModelMesh::setOffset(const G3D::Vector3& value)
|
||||
{
|
||||
if (offset != value)
|
||||
{
|
||||
offset = value;
|
||||
raisePropertyChanged(desc_offset);
|
||||
}
|
||||
}
|
||||
|
||||
void DataModelMesh::setLevelOfDetailX(LODType value)
|
||||
{
|
||||
if (LODx != value)
|
||||
{
|
||||
LODx = value;
|
||||
raisePropertyChanged(desc_levelOfDetailX);
|
||||
}
|
||||
}
|
||||
|
||||
void DataModelMesh::setLevelOfDetailY(LODType value)
|
||||
{
|
||||
if (LODy != value)
|
||||
{
|
||||
LODy = value;
|
||||
raisePropertyChanged(desc_levelOfDetailY);
|
||||
}
|
||||
}
|
||||
} // Namespace Aya
|
||||
69
engine/app/src/DataModel/DataModelMesh.hpp
Normal file
69
engine/app/src/DataModel/DataModelMesh.hpp
Normal file
@@ -0,0 +1,69 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Tree/Instance.hpp"
|
||||
#include "Utility/G3DCore.hpp"
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
extern const char* const sDataModelMesh;
|
||||
|
||||
class DataModelMesh : public DescribedNonCreatable<DataModelMesh, Instance, sDataModelMesh>
|
||||
{
|
||||
public:
|
||||
// Do not change these integer values, they correlate to the enum on the RBXViewNew side
|
||||
enum LODType
|
||||
{
|
||||
LOW_LOD = 0,
|
||||
MEDIUM_LOD = 1,
|
||||
HIGH_LOD = 2
|
||||
};
|
||||
|
||||
protected:
|
||||
Vector3 scale;
|
||||
Vector3 vertColor;
|
||||
Vector3 offset;
|
||||
|
||||
LODType LODx;
|
||||
LODType LODy;
|
||||
|
||||
public:
|
||||
DataModelMesh();
|
||||
|
||||
LODType getLevelOfDetailX() const
|
||||
{
|
||||
return LODx;
|
||||
}
|
||||
void setLevelOfDetailX(LODType val);
|
||||
|
||||
LODType getLevelOfDetailY() const
|
||||
{
|
||||
return LODy;
|
||||
}
|
||||
void setLevelOfDetailY(LODType val);
|
||||
|
||||
const G3D::Vector3& getScale() const
|
||||
{
|
||||
return scale;
|
||||
}
|
||||
void setScale(const G3D::Vector3& value);
|
||||
|
||||
const G3D::Vector3& getVertColor() const
|
||||
{
|
||||
return vertColor;
|
||||
}
|
||||
void setVertColor(const G3D::Vector3& value);
|
||||
|
||||
float getAlpha() const;
|
||||
|
||||
const G3D::Vector3& getOffset() const
|
||||
{
|
||||
return offset;
|
||||
}
|
||||
void setOffset(const G3D::Vector3& value);
|
||||
|
||||
protected:
|
||||
bool askSetParent(const Instance* instance) const;
|
||||
};
|
||||
} // namespace Aya
|
||||
1221
engine/app/src/DataModel/DataStore.cpp
Normal file
1221
engine/app/src/DataModel/DataStore.cpp
Normal file
File diff suppressed because it is too large
Load Diff
203
engine/app/src/DataModel/DataStore.hpp
Normal file
203
engine/app/src/DataModel/DataStore.hpp
Normal file
@@ -0,0 +1,203 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "Reflection/Reflection.hpp"
|
||||
#include "Script/ThreadRef.hpp"
|
||||
#include "Tree/Instance.hpp"
|
||||
#include "RunningAverage.hpp"
|
||||
#include "DataModel/DataStoreService.hpp"
|
||||
#include "Utility/LuaWebService.hpp"
|
||||
#include "signal.h"
|
||||
#include <deque>
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
|
||||
extern const char* const sGlobalDataStore;
|
||||
|
||||
class DataStore : public DescribedNonCreatable<DataStore, Instance, sGlobalDataStore, Reflection::ClassDescriptor::RUNTIME_LOCAL>
|
||||
{
|
||||
typedef DescribedNonCreatable<DataStore, Instance, sGlobalDataStore, Reflection::ClassDescriptor::RUNTIME_LOCAL> Super;
|
||||
|
||||
class CachedRecord
|
||||
{
|
||||
private:
|
||||
Reflection::Variant variant;
|
||||
std::string serialized;
|
||||
Time accessTimeStamp;
|
||||
|
||||
public:
|
||||
CachedRecord() {};
|
||||
|
||||
const Reflection::Variant& getVariant(bool touch = true);
|
||||
const std::string& getSerialized()
|
||||
{
|
||||
return serialized;
|
||||
}
|
||||
const Time& getTime()
|
||||
{
|
||||
return accessTimeStamp;
|
||||
}
|
||||
|
||||
void update(const Reflection::Variant& variant, const std::string& serialized);
|
||||
};
|
||||
|
||||
struct EventSlot
|
||||
{
|
||||
Lua::WeakFunctionRef callback;
|
||||
EventSlot(Lua::WeakFunctionRef);
|
||||
void fire(Reflection::Variant value);
|
||||
};
|
||||
|
||||
bool isLegacy;
|
||||
|
||||
typedef std::map<std::string, CachedRecord> CachedKeys;
|
||||
CachedKeys cachedKeys;
|
||||
typedef std::map<std::string, shared_ptr<Aya::signal<void(Reflection::Variant)>>> OnUpdateKeys;
|
||||
OnUpdateKeys onUpdateKeys;
|
||||
|
||||
typedef std::map<std::string, Time> KeyTimestamps;
|
||||
KeyTimestamps lastSetByKey;
|
||||
|
||||
enum RefetchState
|
||||
{
|
||||
RefetchOnUpdateKeys,
|
||||
RefetchCachedKeys,
|
||||
RefetchDone
|
||||
};
|
||||
|
||||
RefetchState refetchState;
|
||||
std::string nextKeyToRefetch;
|
||||
|
||||
bool backendProcessing;
|
||||
|
||||
std::string constructPostDataForKey(const std::string& key, unsigned index = 0);
|
||||
std::string constructGetUrl();
|
||||
std::string constructSetUrl(const std::string& key, unsigned valueLength);
|
||||
std::string constructSetIfUrl(const std::string& key, unsigned valueLength, unsigned expectedValueLength);
|
||||
std::string constructIncrementUrl(const std::string& key, int delta);
|
||||
|
||||
void processSet(std::string key, std::string* response, std::exception* exception, boost::function<void()> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction);
|
||||
|
||||
void processFetchSingleKey(std::string* response, std::exception* exception, std::string key, bool expectSubKey, boost::function<void()> callback,
|
||||
boost::function<void(std::string)> errorFunction);
|
||||
void lockAcquiredProcessFetchSingleKey(shared_ptr<std::string> response, shared_ptr<std::exception> exception, std::string key, bool expectSubKey,
|
||||
boost::function<void()> callback, boost::function<void(std::string)> errorFunction);
|
||||
|
||||
bool updateCachedKey(const std::string& key, const Reflection::Variant& value);
|
||||
|
||||
void createFetchNewKeyRequest(const std::string& key, boost::function<void()> callback, boost::function<void(std::string)> errorFunction,
|
||||
DataStoreService::HttpRequest& request);
|
||||
|
||||
// Has to be const reference to Lua::WeakFunctionRef so boost::bind won't copy it to pass by value
|
||||
// Lua::WeakFunctionRef needs access to Lua state to copy, so can't be done safely in http threadpool
|
||||
void processSetIf(std::string key, shared_ptr<Lua::WeakFunctionRef> transform, std::string* response, std::exception* exception,
|
||||
boost::function<void(shared_ptr<const Reflection::Tuple>)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
void lockAcquiredProcessSetIf(std::string key, shared_ptr<Lua::WeakFunctionRef> transform, shared_ptr<std::string> response,
|
||||
shared_ptr<std::exception> exception, boost::function<void(shared_ptr<const Reflection::Tuple>)> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction);
|
||||
|
||||
void runTransformFunction(std::string key, const shared_ptr<Lua::WeakFunctionRef> transform,
|
||||
boost::function<void(shared_ptr<const Reflection::Tuple>)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
|
||||
void processFetchCachedKeys(std::string* response, std::exception* exception);
|
||||
void lockAcquiredProcessFetchCachedKeys(shared_ptr<std::string> response, shared_ptr<std::exception> exception);
|
||||
|
||||
bool checkAccess(const std::string& key, boost::function<void(std::string)>* errorFunction);
|
||||
bool checkStudioApiAccess(boost::function<void(std::string)> errorFunction);
|
||||
|
||||
static std::string serializeVariant(const Reflection::Variant& variant, bool* hasNonJsonType);
|
||||
static bool deserializeVariant(const std::string& webValue, Reflection::Variant& result);
|
||||
|
||||
void sendBatchGet(std::stringstream& keysList);
|
||||
void accumulateKeyToFetch(const std::string& key, std::stringstream& keysList, int& counter);
|
||||
|
||||
protected:
|
||||
std::string serviceUrl;
|
||||
std::string name, scope;
|
||||
std::string scopeUrlEncodedIfNeeded, nameUrlEncodedIfNeeded;
|
||||
virtual bool checkValueIsAllowed(const Reflection::Variant&)
|
||||
{
|
||||
return true;
|
||||
};
|
||||
virtual const char* getDataStoreTypeString()
|
||||
{
|
||||
return "standard";
|
||||
}
|
||||
virtual bool queueOrExecuteSet(DataStoreService::HttpRequest& request);
|
||||
|
||||
std::string urlEncodeIfNeeded(const std::string& input);
|
||||
|
||||
public:
|
||||
DataStore(const std::string& name, const std::string& scope, bool legacy);
|
||||
|
||||
static const char* urlApiPath()
|
||||
{
|
||||
return "persistence";
|
||||
}
|
||||
|
||||
void getAsync(std::string key, boost::function<void(Reflection::Variant)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
void updateAsync(std::string key, Lua::WeakFunctionRef transformFunc, boost::function<void(shared_ptr<const Reflection::Tuple>)> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction);
|
||||
void setAsync(
|
||||
std::string key, Reflection::Variant value, boost::function<void()> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
void incrementAsync(
|
||||
std::string key, int delta, boost::function<void(Reflection::Variant)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
|
||||
Aya::signals::connection onUpdate(std::string key, Lua::WeakFunctionRef callback);
|
||||
|
||||
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
|
||||
|
||||
void refetchCachedKeys(int* budget);
|
||||
void resetRefetch();
|
||||
|
||||
bool isKeyThrottled(const std::string& key, Time timestamp);
|
||||
void setKeySetTimestamp(const std::string& key, Time timestamp);
|
||||
DataStoreService* getParentDataStoreService();
|
||||
|
||||
private:
|
||||
void runResumeFunction(std::string key, boost::function<void(Reflection::Variant)> resumeFunction);
|
||||
};
|
||||
|
||||
extern const char* const sOrderedDataStore;
|
||||
class OrderedDataStore : public DescribedNonCreatable<OrderedDataStore, DataStore, sOrderedDataStore, Reflection::ClassDescriptor::RUNTIME_LOCAL>
|
||||
{
|
||||
typedef DescribedNonCreatable<DataStore, DataStore, sOrderedDataStore, Reflection::ClassDescriptor::RUNTIME_LOCAL> Super;
|
||||
|
||||
std::string constructGetSortedUrl(bool isAscending, int pagesize, const double* minValue, const double* maxValue);
|
||||
|
||||
protected:
|
||||
virtual bool checkValueIsAllowed(const Reflection::Variant& v);
|
||||
virtual const char* getDataStoreTypeString()
|
||||
{
|
||||
return "sorted";
|
||||
}
|
||||
virtual bool queueOrExecuteSet(DataStoreService::HttpRequest& request);
|
||||
|
||||
public:
|
||||
OrderedDataStore(const std::string& name, const std::string& scope);
|
||||
|
||||
void getSortedAsync(bool isAscending, int pagesize, Reflection::Variant minValue, Reflection::Variant maxValue,
|
||||
boost::function<void(shared_ptr<Instance>)> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
};
|
||||
|
||||
extern const char* const sDataStorePages;
|
||||
|
||||
class DataStorePages : public DescribedNonCreatable<DataStorePages, Pages, sDataStorePages, Reflection::ClassDescriptor::RUNTIME_LOCAL>
|
||||
{
|
||||
weak_ptr<OrderedDataStore> ds;
|
||||
std::string requestUrl;
|
||||
std::string exclusiveStartKey;
|
||||
|
||||
void processFetch(
|
||||
std::string* response, std::exception* exception, boost::function<void()> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
void lockAcquiredProcessFetch(shared_ptr<std::string> response, shared_ptr<std::exception> exception, boost::function<void()> resumeFunction,
|
||||
boost::function<void(std::string)> errorFunction);
|
||||
|
||||
public:
|
||||
DataStorePages(weak_ptr<OrderedDataStore> ds, const std::string& requestUrl);
|
||||
|
||||
void fetchNextChunk(boost::function<void()> resumeFunction, boost::function<void(std::string)> errorFunction);
|
||||
};
|
||||
} // namespace Aya
|
||||
536
engine/app/src/DataModel/DataStoreService.cpp
Normal file
536
engine/app/src/DataModel/DataStoreService.cpp
Normal file
@@ -0,0 +1,536 @@
|
||||
|
||||
|
||||
#include "DataModel/DataStoreService.hpp"
|
||||
#include "DataModel/DataModel.hpp"
|
||||
#include "DataModel/DataStore.hpp"
|
||||
#include "Players.hpp"
|
||||
|
||||
LOGGROUP(DataStore);
|
||||
|
||||
DYNAMIC_FASTINTVARIABLE(DataStoreJobFrequencyInSeconds, 1);
|
||||
DYNAMIC_FASTINTVARIABLE(DataStoreFetchFrequenceInSeconds, 30);
|
||||
|
||||
DYNAMIC_FASTINTVARIABLE(DataStoreFixedRequestLimit, 60);
|
||||
DYNAMIC_FASTINTVARIABLE(DataStorePerPlayerRequestLimit, 10);
|
||||
DYNAMIC_FASTINTVARIABLE(DataStoreInitialBudget, 100);
|
||||
|
||||
DYNAMIC_FASTINTVARIABLE(DataStoreOrderedSetFixedRequestLimit, 30);
|
||||
DYNAMIC_FASTINTVARIABLE(DataStoreOrderedSetPerPlayerRequestLimit, 5);
|
||||
DYNAMIC_FASTINTVARIABLE(DataStoreOrderedSetInitialBudget, 50);
|
||||
|
||||
DYNAMIC_FASTINTVARIABLE(DataStoreRefetchFixedRequestLimit, 30);
|
||||
DYNAMIC_FASTINTVARIABLE(DataStoreRefetchPerPlayerRequestLimit, 5);
|
||||
|
||||
DYNAMIC_FASTINTVARIABLE(DataStoreSortedFixedRequestLimit, 5);
|
||||
DYNAMIC_FASTINTVARIABLE(DataStoreSortedPerPlayerRequestLimit, 2);
|
||||
DYNAMIC_FASTINTVARIABLE(DataStoreSortedInitialBudget, 10);
|
||||
|
||||
DYNAMIC_FASTINTVARIABLE(DataStoreMaxBudgetMultiplier, 100);
|
||||
|
||||
DYNAMIC_FASTINTVARIABLE(DataStoreMaxThrottledQueue, 30);
|
||||
|
||||
DYNAMIC_FASTINT(DataStoreKeyLengthLimit);
|
||||
|
||||
DYNAMIC_FASTFLAGVARIABLE(GetGlobalDataStorePcallFix, false);
|
||||
|
||||
DYNAMIC_FASTFLAGVARIABLE(UseNewDataStoreLogging, true)
|
||||
|
||||
DYNAMIC_FASTFLAGVARIABLE(UseNewDataStoreRequestSetTimestampBehaviour, true)
|
||||
|
||||
LOGVARIABLE(DataStoreBudget, 0);
|
||||
|
||||
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
const char* const sDataStoreService = "DataStoreService";
|
||||
|
||||
|
||||
static Reflection::BoundFuncDesc<DataStoreService, shared_ptr<Instance>(void)> getGlobalStoreFunction(
|
||||
&DataStoreService::getGlobalDataStore, "GetGlobalDataStore", Security::None);
|
||||
static Reflection::BoundFuncDesc<DataStoreService, shared_ptr<Instance>(std::string, std::string)> getDataStoreFunction(
|
||||
&DataStoreService::getDataStore, "GetDataStore", "name", "scope", "global", Security::None);
|
||||
static Reflection::BoundFuncDesc<DataStoreService, shared_ptr<Instance>(std::string, std::string)> getOrderedDataStore(
|
||||
&DataStoreService::getOrderedDataStore, "GetOrderedDataStore", "name", "scope", "global", Security::None);
|
||||
|
||||
static Reflection::PropDescriptor<DataStoreService, bool> prop_disableUrlEncoding("LegacyNamingScheme", category_Behavior,
|
||||
&DataStoreService::isUrlEncodingDisabled, &DataStoreService::setUrlEncodingDisabled, Reflection::PropertyDescriptor::STANDARD,
|
||||
Security::LocalUser);
|
||||
REFLECTION_END();
|
||||
|
||||
|
||||
class DataStoreJob : public DataModelJob
|
||||
{
|
||||
shared_ptr<DataStoreService> dataStoreService;
|
||||
|
||||
int lastJobFrequency;
|
||||
double desiredHz;
|
||||
|
||||
int secondsSinceLastFetch;
|
||||
|
||||
public:
|
||||
DataStoreJob(DataStoreService* owner)
|
||||
: DataModelJob("DataStoreJob", DataModelJob::Write, false, shared_from(DataModel::get(owner)), Time::Interval(0.01))
|
||||
, dataStoreService(shared_from(owner))
|
||||
{
|
||||
updateHz();
|
||||
secondsSinceLastFetch = 0;
|
||||
|
||||
cyclicExecutive = true;
|
||||
}
|
||||
|
||||
void updateHz()
|
||||
{
|
||||
desiredHz = 1.0f / DFInt::DataStoreJobFrequencyInSeconds;
|
||||
lastJobFrequency = DFInt::DataStoreJobFrequencyInSeconds;
|
||||
}
|
||||
|
||||
/*override*/ Time::Interval sleepTime(const Stats& stats)
|
||||
{
|
||||
return computeStandardSleepTime(stats, desiredHz);
|
||||
}
|
||||
/*override*/ Job::Error error(const Stats& stats)
|
||||
{
|
||||
return computeStandardErrorCyclicExecutiveSleeping(stats, desiredHz);
|
||||
}
|
||||
/*override*/ TaskScheduler::StepResult stepDataModelJob(const Stats& stats)
|
||||
{
|
||||
if (DFInt::DataStoreJobFrequencyInSeconds != lastJobFrequency)
|
||||
updateHz();
|
||||
|
||||
dataStoreService->addThrottlingBudgets(DFInt::DataStoreJobFrequencyInSeconds / 60.0f);
|
||||
dataStoreService->executeThrottledRequests();
|
||||
|
||||
secondsSinceLastFetch += DFInt::DataStoreJobFrequencyInSeconds;
|
||||
if (secondsSinceLastFetch >= DFInt::DataStoreFetchFrequenceInSeconds)
|
||||
{
|
||||
dataStoreService->refetchCachedKeys();
|
||||
secondsSinceLastFetch -= DFInt::DataStoreFetchFrequenceInSeconds;
|
||||
}
|
||||
return TaskScheduler::Stepped;
|
||||
}
|
||||
};
|
||||
|
||||
DataStoreService::DataStoreService()
|
||||
: disableUrlEncoding(false)
|
||||
, msReadSuccessAverageRequestTime()
|
||||
, msErrorAverageRequestTime()
|
||||
, msWriteAverageRequestTime()
|
||||
, msUpdateAverageRequestTime()
|
||||
, msBatchAverageRequestTime()
|
||||
, readSuccessCachedCount(0)
|
||||
{
|
||||
setName(sDataStoreService);
|
||||
throttleCounterGets.addBudget(DFInt::DataStoreInitialBudget, DFInt::DataStoreInitialBudget);
|
||||
throttleCounterGetSorteds.addBudget(DFInt::DataStoreInitialBudget, DFInt::DataStoreInitialBudget);
|
||||
throttleCounterSets.addBudget(DFInt::DataStoreSortedInitialBudget, DFInt::DataStoreSortedInitialBudget);
|
||||
throttleCounterOrderedSets.addBudget(DFInt::DataStoreOrderedSetInitialBudget, DFInt::DataStoreOrderedSetInitialBudget);
|
||||
}
|
||||
|
||||
void DataStoreService::onRequestFinishReport(DataStoreService::HttpRequest* request, bool isError, std::string errorMessage)
|
||||
{
|
||||
unsigned int requestDurationMilliseconds =
|
||||
(unsigned int)((boost::posix_time::microsec_clock::local_time() - request->requestStartTime).total_microseconds() / 1000);
|
||||
|
||||
if (isError)
|
||||
{
|
||||
msErrorAverageRequestTime.incrementValueAverage(requestDurationMilliseconds);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (request->requestType)
|
||||
{
|
||||
case DataStoreService::HttpRequest::RequestType::GET_ASYNC:
|
||||
{
|
||||
msReadSuccessAverageRequestTime.incrementValueAverage(requestDurationMilliseconds);
|
||||
}
|
||||
break;
|
||||
case DataStoreService::HttpRequest::RequestType::UPDATE_ASYNC:
|
||||
{
|
||||
msUpdateAverageRequestTime.incrementValueAverage(requestDurationMilliseconds);
|
||||
}
|
||||
break;
|
||||
case DataStoreService::HttpRequest::RequestType::SET_ASYNC:
|
||||
{
|
||||
msWriteAverageRequestTime.incrementValueAverage(requestDurationMilliseconds);
|
||||
}
|
||||
break;
|
||||
case DataStoreService::HttpRequest::RequestType::INCREMENT_ASYNC:
|
||||
{
|
||||
msWriteAverageRequestTime.incrementValueAverage(requestDurationMilliseconds);
|
||||
}
|
||||
break;
|
||||
case DataStoreService::HttpRequest::RequestType::GET_SORTED_ASYNC_PAGE:
|
||||
{
|
||||
msBatchAverageRequestTime.incrementValueAverage(requestDurationMilliseconds);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reportDataStoreStat(unsigned int& count, unsigned int& average, std::string countReportName, std::string averageReportName)
|
||||
{
|
||||
if (count > 0)
|
||||
{
|
||||
count = 0;
|
||||
average = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
shared_ptr<Instance> DataStoreService::getGlobalDataStore()
|
||||
{
|
||||
return getDataStoreInternal("", "u", true, false);
|
||||
}
|
||||
|
||||
void DataStoreService::onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
|
||||
{
|
||||
if (oldProvider)
|
||||
{
|
||||
FASTLOG1(FLog::DataStore, "Closing down DataStoreService. Backend processing: ", backendProcessing);
|
||||
|
||||
visitChildren(boost::bind(&Instance::unlockParent, _1));
|
||||
if (backendProcessing)
|
||||
{
|
||||
TaskScheduler::singleton().removeBlocking(dataStoreJob);
|
||||
dataStoreJob.reset();
|
||||
}
|
||||
|
||||
backendProcessing = false;
|
||||
}
|
||||
|
||||
Super::onServiceProvider(oldProvider, newProvider);
|
||||
|
||||
if (newProvider)
|
||||
{
|
||||
backendProcessing = Network::Players::backendProcessing(this);
|
||||
FASTLOG1(FLog::DataStore, "Backend processing: %u", backendProcessing);
|
||||
|
||||
if (backendProcessing)
|
||||
{
|
||||
dataStoreJob.reset(new DataStoreJob(this));
|
||||
TaskScheduler::singleton().add(dataStoreJob);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<Instance> DataStoreService::getDataStoreInternal(std::string name, std::string scope, bool legacy, bool ordered)
|
||||
{
|
||||
DataModel* dm = DataModel::get(this);
|
||||
|
||||
if (dm->getPlaceID() == 0)
|
||||
{
|
||||
if (DFFlag::GetGlobalDataStorePcallFix)
|
||||
{
|
||||
throw std::runtime_error("Place has to be opened with Edit button to access DataStores");
|
||||
}
|
||||
StandardOut::singleton()->print(MESSAGE_ERROR, "Place has to be opened with Edit button to access DataStores");
|
||||
return shared_ptr<Instance>();
|
||||
}
|
||||
|
||||
if (!backendProcessing)
|
||||
throw std::runtime_error("DataStore can't be accessed from client");
|
||||
|
||||
AYAASSERT(!(legacy && ordered));
|
||||
|
||||
if (legacy)
|
||||
{
|
||||
if (!legacyDataStore)
|
||||
{
|
||||
FASTLOG(FLog::DataStore, "Creating legacy data store");
|
||||
legacyDataStore = Creatable<Instance>::create<DataStore>(name, scope, true);
|
||||
legacyDataStore->setParent(this);
|
||||
legacyDataStore->lockParent();
|
||||
}
|
||||
return legacyDataStore;
|
||||
}
|
||||
else if (ordered)
|
||||
{
|
||||
StringPair key(name, scope);
|
||||
DataStores::iterator it = orderedDataStores.find(key);
|
||||
if (it == orderedDataStores.end())
|
||||
{
|
||||
FASTLOGS(FLog::DataStore, "Creating data store, name: %s", name);
|
||||
shared_ptr<DataStore> ds = Creatable<Instance>::create<OrderedDataStore>(name, scope);
|
||||
ds->setName(name);
|
||||
ds->setParent(this);
|
||||
ds->lockParent();
|
||||
orderedDataStores[key] = ds;
|
||||
return ds;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
StringPair key(name, scope);
|
||||
DataStores::iterator it = dataStores.find(key);
|
||||
if (it == dataStores.end())
|
||||
{
|
||||
FASTLOGS(FLog::DataStore, "Creating data store, name: %s", name);
|
||||
shared_ptr<DataStore> ds = Creatable<Instance>::create<DataStore>(name, scope, false);
|
||||
ds->setName(name);
|
||||
ds->setParent(this);
|
||||
ds->lockParent();
|
||||
dataStores[key] = ds;
|
||||
return ds;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
void checkNameAndScope(const std::string& name, const std::string& scope)
|
||||
{
|
||||
if (scope.length() == 0)
|
||||
throw std::runtime_error("DataStore scope can't be empty string");
|
||||
|
||||
if (scope.length() > (unsigned)DFInt::DataStoreKeyLengthLimit)
|
||||
throw std::runtime_error("DataStore scope is too long");
|
||||
|
||||
if (name.length() == 0)
|
||||
throw std::runtime_error("DataStore name can't be empty string");
|
||||
|
||||
if (name.length() > (unsigned)DFInt::DataStoreKeyLengthLimit)
|
||||
throw std::runtime_error("DataStore name is too long");
|
||||
}
|
||||
|
||||
|
||||
shared_ptr<Instance> DataStoreService::getDataStore(std::string name, std::string scope)
|
||||
{
|
||||
checkNameAndScope(name, scope);
|
||||
return getDataStoreInternal(name, scope, false, false);
|
||||
}
|
||||
|
||||
shared_ptr<Instance> DataStoreService::getOrderedDataStore(std::string name, std::string scope)
|
||||
{
|
||||
checkNameAndScope(name, scope);
|
||||
return getDataStoreInternal(name, scope, false, true);
|
||||
}
|
||||
|
||||
int DataStoreService::getPlayerNum()
|
||||
{
|
||||
const ServiceProvider* serviceProvider = ServiceProvider::findServiceProvider(this);
|
||||
if (serviceProvider == NULL)
|
||||
{
|
||||
AYAASSERT(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Network::Players* players = serviceProvider->find<Network::Players>();
|
||||
return players ? players->getNumPlayers() : 0;
|
||||
}
|
||||
|
||||
void DataStoreService::addThrottlingBudgets(float timeDeltaMinutes)
|
||||
{
|
||||
int numPlayers = getPlayerNum();
|
||||
|
||||
{
|
||||
int sortedPerPlayerPerMinuteBudget = DFInt::DataStoreSortedPerPlayerRequestLimit * numPlayers + DFInt::DataStoreSortedFixedRequestLimit;
|
||||
float sortedAddedBudget = timeDeltaMinutes * sortedPerPlayerPerMinuteBudget;
|
||||
throttleCounterGetSorteds.addBudget(sortedAddedBudget, DFInt::DataStoreMaxBudgetMultiplier * sortedPerPlayerPerMinuteBudget);
|
||||
}
|
||||
|
||||
{
|
||||
int orderedSetsPerPlayerPerMinuteBudget =
|
||||
DFInt::DataStoreOrderedSetPerPlayerRequestLimit * numPlayers + DFInt::DataStoreOrderedSetFixedRequestLimit;
|
||||
float orderedSetsAddedBudget = timeDeltaMinutes * orderedSetsPerPlayerPerMinuteBudget;
|
||||
throttleCounterOrderedSets.addBudget(orderedSetsAddedBudget, DFInt::DataStoreMaxBudgetMultiplier * orderedSetsPerPlayerPerMinuteBudget);
|
||||
}
|
||||
|
||||
{
|
||||
int perPlayerPerMinuteBudget = DFInt::DataStorePerPlayerRequestLimit * numPlayers + DFInt::DataStoreFixedRequestLimit;
|
||||
float addedBudget = timeDeltaMinutes * perPlayerPerMinuteBudget;
|
||||
|
||||
throttleCounterGets.addBudget(addedBudget, DFInt::DataStoreMaxBudgetMultiplier * perPlayerPerMinuteBudget);
|
||||
throttleCounterSets.addBudget(addedBudget, DFInt::DataStoreMaxBudgetMultiplier * perPlayerPerMinuteBudget);
|
||||
FASTLOG4F(FLog::DataStoreBudget, "Adding budget %f, gets budget: %f, sets budget: %f, sorted budget: %f", addedBudget,
|
||||
throttleCounterGets.getBudget(), throttleCounterSets.getBudget(), throttleCounterGetSorteds.getBudget());
|
||||
}
|
||||
}
|
||||
|
||||
static void dataStorePostSuccess(std::string response, boost::function<void(std::string*, std::exception*)> handler)
|
||||
{
|
||||
handler(&response, 0);
|
||||
}
|
||||
|
||||
static void dataStorePostError(std::string error, boost::function<void(std::string*, std::exception*)> handler)
|
||||
{
|
||||
std::runtime_error e(error.c_str());
|
||||
handler(0, &e);
|
||||
}
|
||||
|
||||
void DataStoreService::HttpRequest::execute(DataStoreService* dataStoreService)
|
||||
{
|
||||
if (DFFlag::UseNewDataStoreRequestSetTimestampBehaviour)
|
||||
{
|
||||
if (requestType == RequestType::SET_ASYNC || requestType == RequestType::INCREMENT_ASYNC || requestType == RequestType::UPDATE_ASYNC)
|
||||
{
|
||||
owner->setKeySetTimestamp(key, Time::nowFast());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (owner)
|
||||
owner->setKeySetTimestamp(key, Time::nowFast());
|
||||
}
|
||||
|
||||
Aya::Http http(url);
|
||||
http.additionalHeaders["Cache-Control"] = "no-cache";
|
||||
http.doNotUseCachedResponse = true;
|
||||
http.post(postData.size() == 0 ? " " : postData, Http::kContentTypeUrlEncoded, false, handler);
|
||||
}
|
||||
|
||||
bool DataStoreService::HttpRequest::isKeyThrottled(Time timestamp)
|
||||
{
|
||||
if (owner == NULL)
|
||||
return false;
|
||||
|
||||
return owner->isKeyThrottled(key, timestamp);
|
||||
}
|
||||
|
||||
bool DataStoreService::queueOrExecuteRequest(HttpRequest& request, std::list<HttpRequest>& queue, BudgetedThrottlingHelper& helper)
|
||||
{
|
||||
FASTLOG1(FLog::DataStore, "Queue suze: %u", queue.size());
|
||||
if (request.isKeyThrottled(Time::nowFast()) || !helper.checkAndReduceBudget())
|
||||
{
|
||||
FASTLOG1F(FLog::DataStore, "Throttling, budget: %f", helper.getBudget());
|
||||
if (queue.size() < (unsigned)DFInt::DataStoreMaxThrottledQueue)
|
||||
{
|
||||
queue.push_back(request);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
FASTLOG1F(FLog::DataStore, "Not throttling, budget after: %f, queue size: %f", helper.getBudget());
|
||||
request.execute(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DataStoreService::queueOrExecuteGet(DataStore* source, HttpRequest& request)
|
||||
{
|
||||
DataStoreService* service = fastDynamicCast<DataStoreService>(source->getParent());
|
||||
AYAASSERT(service);
|
||||
if (!service)
|
||||
return false;
|
||||
|
||||
return service->queueOrExecuteRequest(request, service->throttledGets, service->throttleCounterGets);
|
||||
}
|
||||
|
||||
bool DataStoreService::queueOrExecuteGetSorted(DataStore* source, HttpRequest& request)
|
||||
{
|
||||
DataStoreService* service = fastDynamicCast<DataStoreService>(source->getParent());
|
||||
AYAASSERT(service);
|
||||
if (!service)
|
||||
return false;
|
||||
|
||||
return service->queueOrExecuteRequest(request, service->throttledGetSorteds, service->throttleCounterGetSorteds);
|
||||
}
|
||||
|
||||
bool DataStoreService::queueOrExecuteSet(DataStore* source, HttpRequest& request)
|
||||
{
|
||||
DataStoreService* service = fastDynamicCast<DataStoreService>(source->getParent());
|
||||
AYAASSERT(service);
|
||||
if (!service)
|
||||
return false;
|
||||
|
||||
return service->queueOrExecuteRequest(request, service->throttledSets, service->throttleCounterSets);
|
||||
}
|
||||
|
||||
bool DataStoreService::queueOrExecuteOrderedSet(DataStore* source, HttpRequest& request)
|
||||
{
|
||||
DataStoreService* service = fastDynamicCast<DataStoreService>(source->getParent());
|
||||
AYAASSERT(service);
|
||||
if (!service)
|
||||
return false;
|
||||
|
||||
return service->queueOrExecuteRequest(request, service->throttledOrderedSets, service->throttleCounterOrderedSets);
|
||||
}
|
||||
|
||||
void DataStoreService::executeThrottledRequests(std::list<HttpRequest>& queue, BudgetedThrottlingHelper& helper)
|
||||
{
|
||||
if (queue.size() > 0)
|
||||
FASTLOG2F(FLog::DataStore, "Executing throttled requests, size: %f, budget: %f", (float)queue.size(), helper.getBudget());
|
||||
|
||||
Time now = Time::nowFast();
|
||||
|
||||
for (std::list<HttpRequest>::iterator it = queue.begin(); it != queue.end();)
|
||||
{
|
||||
if (it->isKeyThrottled(now))
|
||||
{
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!helper.checkAndReduceBudget())
|
||||
break;
|
||||
|
||||
it->execute(this);
|
||||
it = queue.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void DataStoreService::executeThrottledRequests()
|
||||
{
|
||||
executeThrottledRequests(throttledGets, throttleCounterGets);
|
||||
executeThrottledRequests(throttledGetSorteds, throttleCounterGetSorteds);
|
||||
executeThrottledRequests(throttledSets, throttleCounterSets);
|
||||
executeThrottledRequests(throttledOrderedSets, throttleCounterOrderedSets);
|
||||
}
|
||||
|
||||
void DataStoreService::setUrlEncodingDisabled(bool disabled)
|
||||
{
|
||||
if (disabled != disableUrlEncoding)
|
||||
{
|
||||
disableUrlEncoding = disabled;
|
||||
}
|
||||
}
|
||||
|
||||
void DataStoreService::refetchCachedKeys()
|
||||
{
|
||||
int budget = (DFInt::DataStoreRefetchFixedRequestLimit + DFInt::DataStoreRefetchPerPlayerRequestLimit * getPlayerNum()) *
|
||||
DFInt::DataStoreFetchFrequenceInSeconds / 60;
|
||||
|
||||
FASTLOG1(FLog::DataStore, "Initial budget for refetch: %i", budget);
|
||||
|
||||
if (legacyDataStore)
|
||||
legacyDataStore->refetchCachedKeys(&budget);
|
||||
|
||||
for (DataStores::iterator it = dataStores.begin(); it != dataStores.end(); ++it)
|
||||
{
|
||||
if (budget < 0)
|
||||
break;
|
||||
|
||||
it->second->refetchCachedKeys(&budget);
|
||||
}
|
||||
|
||||
for (DataStores::iterator it = orderedDataStores.begin(); it != orderedDataStores.end(); ++it)
|
||||
{
|
||||
if (budget < 0)
|
||||
break;
|
||||
|
||||
it->second->refetchCachedKeys(&budget);
|
||||
}
|
||||
|
||||
if (budget >= 0)
|
||||
{
|
||||
FASTLOG1(FLog::DataStore, "Refetch budget is %i, so reset refetch on all DataStores", budget);
|
||||
|
||||
if (legacyDataStore)
|
||||
legacyDataStore->resetRefetch();
|
||||
|
||||
for (DataStores::iterator it = dataStores.begin(); it != dataStores.end(); ++it)
|
||||
{
|
||||
it->second->resetRefetch();
|
||||
}
|
||||
|
||||
for (DataStores::iterator it = orderedDataStores.begin(); it != orderedDataStores.end(); ++it)
|
||||
{
|
||||
it->second->resetRefetch();
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace Aya
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user