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

238 lines
7.2 KiB
C++

#include "stdafx.h"
#include "V8DataModel/TextureTrail.h"
#include "GfxBase/Adorn.h"
#include "GfxBase/TextureProxyBase.h"
#include "rbx/rbxTime.h"
#include "reflection/reflection.h"
#include "util/TextureId.h"
#include "V8DataModel/Camera.h"
#include "V8DataModel/GuiBase3d.h"
#include "V8DataModel/PartInstance.h"
#include "V8DataModel/Workspace.h"
#include "V8Tree/Service.h"
namespace RBX {
const char* const sTextureTrail = "TextureTrail";
Reflection::RefPropDescriptor<TextureTrail, PartInstance>
TextureTrail::prop_From("From", category_Data,
&TextureTrail::getFrom, &TextureTrail::setFrom,
Reflection::PropertyDescriptor::STANDARD, Security::RobloxPlace);
Reflection::RefPropDescriptor<TextureTrail, PartInstance>
TextureTrail::prop_To("To", category_Data,
&TextureTrail::getTo, &TextureTrail::setTo,
Reflection::PropertyDescriptor::STANDARD, Security::RobloxPlace);
Reflection::PropDescriptor<TextureTrail, TextureId>
TextureTrail::prop_Texture("Texture", category_Appearance,
&TextureTrail::getTexture, &TextureTrail::setTexture,
Reflection::PropertyDescriptor::STANDARD, Security::RobloxPlace);
Reflection::PropDescriptor<TextureTrail, Vector2>
TextureTrail::prop_TextureSize("TextureSize", category_Appearance,
&TextureTrail::getTextureSize, &TextureTrail::setTextureSize,
Reflection::PropertyDescriptor::STANDARD, Security::RobloxPlace);
Reflection::PropDescriptor<TextureTrail, float>
TextureTrail::prop_Velocity("Velocity", category_Data,
&TextureTrail::getVelocity, &TextureTrail::setVelocity,
Reflection::PropertyDescriptor::STANDARD, Security::RobloxPlace);
Reflection::PropDescriptor<TextureTrail, float>
TextureTrail::prop_StudsBetweenTextures("StudsBetweenTextures", category_Data,
&TextureTrail::getStudsBetweenTextures,
&TextureTrail::setStudsBetweenTextures,
Reflection::PropertyDescriptor::STANDARD, Security::RobloxPlace);
Reflection::PropDescriptor<TextureTrail, float>
TextureTrail::prop_CycleOffset("CycleOffset", category_Data,
&TextureTrail::getCycleOffset, &TextureTrail::setCycleOffset,
Reflection::PropertyDescriptor::STANDARD, Security::RobloxPlace);
TextureTrail::TextureTrail()
: DescribedCreatable<TextureTrail, GuiBase3d, sTextureTrail>(sTextureTrail),
textureSize(1, 1),
velocity(2.0f),
studsBetweenTextures(4.0f),
cycleOffset(0.0f)
{
}
void TextureTrail::setFrom(PartInstance* value) {
setPartInstance(from, value, prop_From);
}
PartInstance* TextureTrail::getFrom() const {
return from.lock().get();
}
void TextureTrail::setTo(PartInstance* value) {
setPartInstance(to, value, prop_To);
}
PartInstance* TextureTrail::getTo() const {
return to.lock().get();
}
void TextureTrail::setTexture(TextureId texture) {
this->texture = texture;
}
TextureId TextureTrail::getTexture() const {
return texture;
}
void TextureTrail::setTextureSize(Vector2 textureSize) {
this->textureSize.x = textureSize.x;
this->textureSize.y = textureSize.y;
}
Vector2 TextureTrail::getTextureSize() const {
return textureSize;
}
void TextureTrail::setVelocity(float velocity) {
this->velocity = velocity;
}
float TextureTrail::getVelocity() const {
return velocity;
}
void TextureTrail::setStudsBetweenTextures(float spacing) {
this->studsBetweenTextures = spacing;
}
float TextureTrail::getStudsBetweenTextures() const {
return studsBetweenTextures;
}
void TextureTrail::setCycleOffset(float cycleOffset) {
this->cycleOffset = cycleOffset;
}
float TextureTrail::getCycleOffset() const {
return cycleOffset;
}
void TextureTrail::render3dAdorn(Adorn *adorn) {
Vector3 fromPosition, toPosition;
if (!getPosition(from, &fromPosition) || !getPosition(to, &toPosition)) {
return;
}
if (texture.isNull()) {
return;
}
Workspace* workspace = ServiceProvider::find<Workspace>(this);
if (!workspace) {
return;
}
const Camera* camera = workspace->getConstCamera();
if (!camera) {
return;
}
renderInternal(workspace, camera, fromPosition, toPosition, texture, getFullName() + ".Texture",
textureSize, velocity, studsBetweenTextures, cycleOffset, Color4(color, 1.0f - getTransparency()), adorn);
}
void TextureTrail::renderInternal(
const Workspace* workspace, const Camera* camera,
const Vector3& fromPosition, const Vector3& toPosition,
const TextureId& texture, std::string context, const Vector2& textureSize,
float velocity, float studsBetweenTextures, float cycleOffset,
const Color4& color, Adorn* adorn) {
// create a coordinate frame centered on From with -z pointed at To
CoordinateFrame trailOnFrom(fromPosition);
trailOnFrom.lookAt(toPosition);
float distance = (fromPosition - toPosition).length();
CoordinateFrame cameraInObjectSpace(
trailOnFrom.toObjectSpace(camera->getCameraCoordinateFrame()));
float spaceBetweenIcons = studsBetweenTextures;
double gameTime = Time::now<Time::Fast>().timestampSeconds();
// calculate an offset based on time to make the textures move along
// the line.
// Velocity is measured in studs per second.
// CycleOffset is treated as fraction of the space between textures
// (-1 to 1) to move the texture forward or backward in the loop.
float timeOffset = spaceBetweenIcons *
fmod((velocity / spaceBetweenIcons) * gameTime + cycleOffset, 1);
bool waitingFallback;
TextureProxyBaseRef textureRef =
adorn->createTextureProxy(texture, waitingFallback, true /* blocking */, context);
adorn->setTexture(0, textureRef);
bool cameraPositiveX = cameraInObjectSpace.translation.x > 0;
float halfWidth = textureSize.x / 2;
float halfHeight = textureSize.y / 2;
for (float i = 0; i < distance + spaceBetweenIcons; i += spaceBetweenIcons) {
// build icons starting at "To" so that the stream will look anchored on
// that end
float zLoc = distance - i + timeOffset;
if (zLoc >= 0 && zLoc <= distance) {
CoordinateFrame objectLocationLookingAtCamera(Vector3(0, 0, -zLoc));
objectLocationLookingAtCamera.lookAt(cameraInObjectSpace.translation,
Vector3::unitZ());
Vector2 topLeft, bottomRight;
if (cameraPositiveX) {
topLeft = Vector2(0, 0);
bottomRight = Vector2(1, 1);
} else {
// The look at function will cause the sprite to be upside down
// when the camera is in negative X space. To compensate,
// invert the Y axis of the sprite (the X axis is maintained,
// so that if a sprite has left-right directionality, that will
// be preserved).
topLeft = Vector2(0, 1);
bottomRight = Vector2(1, 0);
}
adorn->setObjectToWorldMatrix(trailOnFrom * objectLocationLookingAtCamera);
adorn->quad(
Vector3(-halfHeight, halfWidth, 0),
Vector3(-halfHeight, -halfWidth, 0),
Vector3( halfHeight, halfWidth, 0),
Vector3( halfHeight, -halfWidth, 0),
color, topLeft, bottomRight);
}
}
// clear texture; if this is not done, other adorns may accidentally
// gain a texture
adorn->setTexture(0, TextureProxyBaseRef());
}
void TextureTrail::setPartInstance(weak_ptr<PartInstance>& data,
PartInstance* newValue, const PartProp& prop) {
if(data.lock().get() != newValue){
data = shared_from(newValue);
raisePropertyChanged(prop);
}
}
bool TextureTrail::getPosition(const weak_ptr<PartInstance>& dataSource,
Vector3* out) {
PartInstance* dataPtr = dataSource.lock().get();
if (dataPtr) {
(*out) = dataPtr->getTranslationUi();
return true;
}
return false;
}
}