Working on path visualization
This commit is contained in:
1 parent
96e748715b
commit
0240721d46
12 files changed
+335
-3
No files matched your search
@@ -60,6 +60,8 @@ add_library(main SHARED
|
||||
../../../../src/Localization.h
|
||||
../../../../src/render/Renderer.cpp
|
||||
../../../../src/render/Renderer.h
|
||||
../../../../src/render/PathVisualization.cpp
|
||||
../../../../src/render/PathVisualization.h
|
||||
../../../../src/render/ShaderManager.cpp
|
||||
../../../../src/render/ShaderManager.h
|
||||
../../../../src/render/TextureManager.cpp
|
||||
|
||||
@@ -85,6 +85,8 @@ add_executable(ShadowOverBishkekDemo
|
||||
../src/Localization.h
|
||||
../src/render/Renderer.cpp
|
||||
../src/render/Renderer.h
|
||||
../src/render/PathVisualization.cpp
|
||||
../src/render/PathVisualization.h
|
||||
../src/render/ShaderManager.cpp
|
||||
../src/render/ShaderManager.h
|
||||
../src/render/TextureManager.cpp
|
||||
|
||||
@@ -79,6 +79,8 @@ set(SOURCES
|
||||
../src/Localization.h
|
||||
../src/render/Renderer.cpp
|
||||
../src/render/Renderer.h
|
||||
../src/render/PathVisualization.cpp
|
||||
../src/render/PathVisualization.h
|
||||
../src/render/ShaderManager.cpp
|
||||
../src/render/ShaderManager.h
|
||||
../src/render/TextureManager.cpp
|
||||
|
||||
@@ -27,6 +27,8 @@ add_executable(ShadowOverBishkekDemo WIN32
|
||||
../src/Localization.h
|
||||
../src/render/Renderer.cpp
|
||||
../src/render/Renderer.h
|
||||
../src/render/PathVisualization.cpp
|
||||
../src/render/PathVisualization.h
|
||||
../src/render/ShaderManager.cpp
|
||||
../src/render/ShaderManager.h
|
||||
../src/render/TextureManager.cpp
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
attribute vec3 vPosition;
|
||||
|
||||
uniform mat4 ProjectionModelViewMatrix;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = ProjectionModelViewMatrix * vec4(vPosition, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
uniform vec4 uColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_FragColor = uColor;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
precision mediump float;
|
||||
|
||||
uniform vec4 uColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_FragColor = uColor;
|
||||
}
|
||||
@@ -251,6 +251,10 @@ namespace FRG
|
||||
renderer.shaderManager.AddShaderFromFiles("fog_shadow_inst", "resources/shaders/fog_shadow_web.vertex", "resources/shaders/fog_shadow_web.fragment", CONST_ZIP_FILE, "#define INSTANCED\n");
|
||||
renderer.shaderManager.AddShaderFromFiles("night_fog_inst", "resources/shaders/night_fog_web.vertex", "resources/shaders/night_fog_web.fragment", CONST_ZIP_FILE, "#define INSTANCED\n");
|
||||
renderer.shaderManager.AddShaderFromFiles("night_fog_shadow_inst", "resources/shaders/night_fog_shadow_web.vertex", "resources/shaders/night_fog_shadow_web.fragment", CONST_ZIP_FILE, "#define INSTANCED\n");
|
||||
renderer.shaderManager.AddShaderFromFiles("path_visualization",
|
||||
"resources/shaders/path_visualization.vertex",
|
||||
"resources/shaders/path_visualization_web.fragment",
|
||||
CONST_ZIP_FILE);
|
||||
|
||||
renderer.shaderManager.AddShaderFromFiles("text2d",
|
||||
"resources/shaders/text2d.vertex",
|
||||
@@ -318,6 +322,10 @@ namespace FRG
|
||||
renderer.shaderManager.AddShaderFromFiles("fog_shadow_inst", "resources/shaders/fog_shadow.vertex", "resources/shaders/fog_shadow_desktop.fragment", CONST_ZIP_FILE, "#define INSTANCED\n");
|
||||
renderer.shaderManager.AddShaderFromFiles("night_fog_inst", "resources/shaders/night_fog.vertex", "resources/shaders/night_fog_desktop.fragment", CONST_ZIP_FILE, "#define INSTANCED\n");
|
||||
renderer.shaderManager.AddShaderFromFiles("night_fog_shadow_inst", "resources/shaders/night_fog_shadow.vertex", "resources/shaders/night_fog_shadow_desktop.fragment", CONST_ZIP_FILE, "#define INSTANCED\n");
|
||||
renderer.shaderManager.AddShaderFromFiles("path_visualization",
|
||||
"resources/shaders/path_visualization.vertex",
|
||||
"resources/shaders/path_visualization_desktop.fragment",
|
||||
CONST_ZIP_FILE);
|
||||
|
||||
renderer.shaderManager.AddShaderFromFiles("text2d",
|
||||
"resources/shaders/text2d.vertex",
|
||||
|
||||
+25
-3
@@ -104,6 +104,7 @@ namespace FRG
|
||||
|
||||
void Location::setup(const LocationSetup& params, Quest::QuestJournal* journal, AudioPlayerAsync* audioPlayer)
|
||||
{
|
||||
pathVisualization.initialize();
|
||||
|
||||
// Load static game objects
|
||||
{
|
||||
@@ -708,6 +709,11 @@ namespace FRG
|
||||
renderer.shaderManager.PopShader();
|
||||
}
|
||||
|
||||
void Location::setPathVisualizationEnabled(bool enabled)
|
||||
{
|
||||
pathVisualization.setEnabled(enabled);
|
||||
}
|
||||
|
||||
void Location::drawGame()
|
||||
{
|
||||
|
||||
@@ -766,6 +772,7 @@ namespace FRG
|
||||
drawNpcCar();
|
||||
|
||||
for (auto& tz : teleportZones) tz.draw(renderer, Environment::zoom, Environment::width, Environment::height);
|
||||
pathVisualization.draw(renderer);
|
||||
|
||||
if (editorMode == EditorMode::Navigation) {
|
||||
editor.drawNavigation();
|
||||
@@ -986,6 +993,7 @@ namespace FRG
|
||||
|
||||
for (auto& tz : teleportZones) tz.draw(renderer, Environment::zoom, Environment::width, Environment::height);
|
||||
#endif
|
||||
pathVisualization.draw(renderer);
|
||||
|
||||
if (editorMode == EditorMode::Navigation) {
|
||||
editor.drawNavigation();
|
||||
@@ -1068,6 +1076,7 @@ namespace FRG
|
||||
//drawNpcCar();
|
||||
|
||||
for (auto& tz : teleportZones) tz.draw(renderer, Environment::zoom, Environment::width, Environment::height);
|
||||
pathVisualization.draw(renderer);
|
||||
|
||||
if (editorMode == EditorMode::Navigation) {
|
||||
editor.drawNavigation();
|
||||
@@ -1375,6 +1384,7 @@ namespace FRG
|
||||
drawNpcCar();
|
||||
|
||||
for (auto& tz : teleportZones) tz.draw(renderer, Environment::zoom, Environment::width, Environment::height);
|
||||
pathVisualization.draw(renderer);
|
||||
|
||||
if (editorMode == EditorMode::Navigation) {
|
||||
editor.drawNavigation();
|
||||
@@ -1669,6 +1679,8 @@ namespace FRG
|
||||
|
||||
void Location::update(int64_t delta)
|
||||
{
|
||||
pathVisualization.update(delta);
|
||||
|
||||
for (auto& intObj : interactiveObjects) {
|
||||
intObj.update(delta);
|
||||
}
|
||||
@@ -1986,6 +1998,10 @@ namespace FRG
|
||||
return;
|
||||
}
|
||||
|
||||
// Any new gameplay click invalidates the previous click feedback. Ground
|
||||
// clicks and interactive-object clicks that require walking start a replacement.
|
||||
pathVisualization.cancel();
|
||||
|
||||
//logger() << "[CLICK] Camera position: (" << camPos.x() << ", " << camPos.y() << ", " << camPos.z() << ")" << std::endl;
|
||||
//logger() << "[CLICK] Ray direction: (" << rayDir.x() << ", " << rayDir.y() << ", " << rayDir.z() << ")" << std::endl;
|
||||
|
||||
@@ -2001,9 +2017,13 @@ namespace FRG
|
||||
state.targetInteractiveObjectIndex = findInteractiveObjectIndex(clickedObject);
|
||||
state.targetInteractNpcIndex = -1;
|
||||
state.targetTeleportZoneIndex = -1;
|
||||
player->setTarget(clickedObject->state.hasInteractionPosition
|
||||
const Eigen::Vector3f interactionTarget = clickedObject->state.hasInteractionPosition
|
||||
? clickedObject->state.interactionPosition
|
||||
: clickedObject->state.position);
|
||||
: clickedObject->state.position;
|
||||
player->setTarget(interactionTarget);
|
||||
if (!player->state.pathWaypoints.empty()) {
|
||||
pathVisualization.start(*player, interactionTarget);
|
||||
}
|
||||
player->state.attackTargetIndex = CharacterState::kNoTarget;
|
||||
//logger() << "[CLICK] Player moving to object..." << std::endl;
|
||||
}
|
||||
@@ -2087,7 +2107,9 @@ namespace FRG
|
||||
player->setTarget(clickedTeleport->position);
|
||||
player->state.attackTargetIndex = CharacterState::kNoTarget;
|
||||
} else {
|
||||
player->setTarget(Eigen::Vector3f(hit.x(), 0.f, hit.z()));
|
||||
const Eigen::Vector3f clickedDestination(hit.x(), 0.f, hit.z());
|
||||
player->setTarget(clickedDestination);
|
||||
pathVisualization.start(*player, clickedDestination);
|
||||
player->state.attackTargetIndex = CharacterState::kNoTarget;
|
||||
state.targetInteractNpcIndex = -1;
|
||||
state.targetTeleportZoneIndex = -1;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "items/InteractiveObject.h"
|
||||
#include "navigation/PathFinder.h"
|
||||
#include "render/ShadowMap.h"
|
||||
#include "render/PathVisualization.h"
|
||||
|
||||
#include "ScriptEngine.h"
|
||||
#include "dialogue/DialogueSystem.h"
|
||||
@@ -178,6 +179,10 @@ namespace FRG
|
||||
|
||||
void update(int64_t deltaMs);
|
||||
void updateNpcCar(int64_t deltaMs);
|
||||
// Purely visual feedback for click-triggered player routes. Disabling it
|
||||
// also cancels an animation that is currently playing.
|
||||
void setPathVisualizationEnabled(bool enabled);
|
||||
bool isPathVisualizationEnabled() const { return pathVisualization.isEnabled(); }
|
||||
|
||||
void handleDown(int64_t fingerId, int eventX, int eventY, int mx, int my);
|
||||
void handleUp(int64_t fingerId, int mx, int my);
|
||||
@@ -228,6 +233,8 @@ namespace FRG
|
||||
int findNpcIndex(const Character* npc) const;
|
||||
void fireBumpCallbacks(Character* mover, Character* standing);
|
||||
|
||||
PathVisualization pathVisualization;
|
||||
|
||||
std::unordered_map<Character*, Eigen::Vector3f> lastCharacterPositions;
|
||||
std::unordered_map<Character*, int64_t> replanCooldownRemainingMs;
|
||||
std::unordered_map<Character*, int> stuckFrameCounts;
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
#include "render/PathVisualization.h"
|
||||
|
||||
#include "Character.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
namespace FRG {
|
||||
|
||||
namespace {
|
||||
constexpr float kPi = 3.14159265358979323846f;
|
||||
}
|
||||
|
||||
void PathVisualization::initialize()
|
||||
{
|
||||
if (!dotMesh.data.PositionData.empty()) return;
|
||||
|
||||
constexpr int kSphereSegments = 8;
|
||||
constexpr int kSphereRings = 3;
|
||||
constexpr float kSphereRadius = 0.06f;
|
||||
VertexDataStruct dotData;
|
||||
dotData.PositionData.reserve(kSphereSegments * (2 + (kSphereRings - 1) * 2) * 3);
|
||||
const auto spherePoint = [=](float phi, float angle) {
|
||||
const float ringRadius = std::sin(phi) * kSphereRadius;
|
||||
return Eigen::Vector3f(
|
||||
std::cos(angle) * ringRadius,
|
||||
kSphereRadius + std::cos(phi) * kSphereRadius,
|
||||
std::sin(angle) * ringRadius);
|
||||
};
|
||||
const Eigen::Vector3f sphereTop(0.0f, kSphereRadius * 2.0f, 0.0f);
|
||||
const Eigen::Vector3f sphereBottom(0.0f, 0.0f, 0.0f);
|
||||
for (int i = 0; i < kSphereSegments; ++i) {
|
||||
const float a0 = static_cast<float>(i) * 2.0f * kPi / kSphereSegments;
|
||||
const float a1 = static_cast<float>(i + 1) * 2.0f * kPi / kSphereSegments;
|
||||
const Eigen::Vector3f first0 = spherePoint(kPi / 4.0f, a0);
|
||||
const Eigen::Vector3f first1 = spherePoint(kPi / 4.0f, a1);
|
||||
dotData.PositionData.insert(dotData.PositionData.end(), { sphereTop, first1, first0 });
|
||||
|
||||
Eigen::Vector3f previous0 = first0;
|
||||
Eigen::Vector3f previous1 = first1;
|
||||
for (int ring = 2; ring <= kSphereRings; ++ring) {
|
||||
const float phi = static_cast<float>(ring) * kPi / 4.0f;
|
||||
const Eigen::Vector3f current0 = spherePoint(phi, a0);
|
||||
const Eigen::Vector3f current1 = spherePoint(phi, a1);
|
||||
dotData.PositionData.insert(dotData.PositionData.end(), {
|
||||
previous0, previous1, current1,
|
||||
previous0, current1, current0
|
||||
});
|
||||
previous0 = current0;
|
||||
previous1 = current1;
|
||||
}
|
||||
dotData.PositionData.insert(dotData.PositionData.end(), { sphereBottom, previous0, previous1 });
|
||||
}
|
||||
dotMesh.AssignFrom(dotData);
|
||||
|
||||
constexpr float kHalfWidth = 0.125f;
|
||||
constexpr float kHeight = 0.40f;
|
||||
const Eigen::Vector3f tip(0.0f, 0.0f, 0.0f);
|
||||
const Eigen::Vector3f c0(-kHalfWidth, kHeight, -kHalfWidth);
|
||||
const Eigen::Vector3f c1( kHalfWidth, kHeight, -kHalfWidth);
|
||||
const Eigen::Vector3f c2( kHalfWidth, kHeight, kHalfWidth);
|
||||
const Eigen::Vector3f c3(-kHalfWidth, kHeight, kHalfWidth);
|
||||
VertexDataStruct destinationData;
|
||||
destinationData.PositionData = {
|
||||
tip, c1, c0, tip, c2, c1, tip, c3, c2, tip, c0, c3,
|
||||
c0, c1, c2, c0, c2, c3
|
||||
};
|
||||
destinationMesh.AssignFrom(destinationData);
|
||||
}
|
||||
|
||||
void PathVisualization::setEnabled(bool value)
|
||||
{
|
||||
enabled = value;
|
||||
if (!enabled) cancel();
|
||||
}
|
||||
|
||||
void PathVisualization::cancel()
|
||||
{
|
||||
active = false;
|
||||
elapsedMs = 0.0f;
|
||||
durationMs = 0.0f;
|
||||
dots.clear();
|
||||
}
|
||||
|
||||
void PathVisualization::start(const Character& player, const Eigen::Vector3f& clickedDestination)
|
||||
{
|
||||
cancel();
|
||||
if (!enabled) return;
|
||||
|
||||
std::vector<Eigen::Vector3f> route;
|
||||
route.reserve(player.state.pathWaypoints.size() + 1);
|
||||
route.push_back(Eigen::Vector3f(player.state.position.x(), 0.0f, player.state.position.z()));
|
||||
const size_t firstWaypoint = (std::min)(
|
||||
player.state.currentWaypointIndex, player.state.pathWaypoints.size());
|
||||
for (size_t i = firstWaypoint; i < player.state.pathWaypoints.size(); ++i) {
|
||||
const Eigen::Vector3f& waypoint = player.state.pathWaypoints[i];
|
||||
route.push_back(Eigen::Vector3f(waypoint.x(), 0.0f, waypoint.z()));
|
||||
}
|
||||
|
||||
destination = route.size() > 1
|
||||
? route.back()
|
||||
: Eigen::Vector3f(clickedDestination.x(), 0.0f, clickedDestination.z());
|
||||
|
||||
float totalLength = 0.0f;
|
||||
for (size_t i = 1; i < route.size(); ++i) {
|
||||
totalLength += (route[i] - route[i - 1]).norm();
|
||||
}
|
||||
|
||||
if (totalLength > 0.001f) {
|
||||
constexpr size_t kMaxDots = 360;
|
||||
const float spacing = (std::max)(0.24f, totalLength / static_cast<float>(kMaxDots));
|
||||
constexpr float kMaximumRevealMs = 300.0f;
|
||||
constexpr float kMaximumDotLifetimeMs = 580.0f;
|
||||
constexpr float kPlayerClearanceMs = 20.0f;
|
||||
constexpr float kPlayerMaximumSpeedMultiplier = 1.5f;
|
||||
const float playerSpeed = (std::max)(
|
||||
player.state.walkSpeed * kPlayerMaximumSpeedMultiplier, 0.01f);
|
||||
const float playerRouteTimeMs = totalLength / playerSpeed * 1000.0f;
|
||||
const float revealDurationMs = (std::min)(kMaximumRevealMs, playerRouteTimeMs / 3.0f);
|
||||
const float waveSpeed = totalLength / (revealDurationMs / 1000.0f);
|
||||
float traversed = 0.0f;
|
||||
float nextDotDistance = spacing * 0.65f;
|
||||
|
||||
for (size_t i = 1; i < route.size() && dots.size() < kMaxDots; ++i) {
|
||||
const Eigen::Vector3f segment = route[i] - route[i - 1];
|
||||
const float segmentLength = segment.norm();
|
||||
if (segmentLength <= 0.001f) continue;
|
||||
|
||||
while (nextDotDistance < totalLength - spacing * 0.25f &&
|
||||
nextDotDistance <= traversed + segmentLength &&
|
||||
dots.size() < kMaxDots) {
|
||||
const float t = (nextDotDistance - traversed) / segmentLength;
|
||||
Dot dot;
|
||||
dot.position = route[i - 1] + segment * t;
|
||||
dot.delayMs = nextDotDistance / waveSpeed * 1000.0f;
|
||||
const float playerArrivalMs = nextDotDistance / playerSpeed * 1000.0f;
|
||||
dot.lifetimeMs = (std::max)(0.0f, (std::min)(
|
||||
kMaximumDotLifetimeMs,
|
||||
playerArrivalMs - dot.delayMs - kPlayerClearanceMs));
|
||||
dots.push_back(dot);
|
||||
nextDotDistance += spacing;
|
||||
}
|
||||
traversed += segmentLength;
|
||||
}
|
||||
}
|
||||
|
||||
durationMs = 900.0f;
|
||||
elapsedMs = 0.0f;
|
||||
active = true;
|
||||
}
|
||||
|
||||
void PathVisualization::update(int64_t deltaMs)
|
||||
{
|
||||
if (!active) return;
|
||||
elapsedMs += static_cast<float>(deltaMs);
|
||||
if (elapsedMs >= durationMs) cancel();
|
||||
}
|
||||
|
||||
void PathVisualization::draw(Renderer& renderer)
|
||||
{
|
||||
if (!enabled || !active) return;
|
||||
|
||||
const auto smoothStep = [](float t) {
|
||||
t = std::clamp(t, 0.0f, 1.0f);
|
||||
return t * t * (3.0f - 2.0f * t);
|
||||
};
|
||||
const auto dotAlpha = [&](float localAgeMs, float lifetimeMs) {
|
||||
if (lifetimeMs <= 0.0f || localAgeMs < 0.0f || localAgeMs >= lifetimeMs) return 0.0f;
|
||||
const float fadeInMs = (std::min)(55.0f, lifetimeMs * 0.35f);
|
||||
if (localAgeMs < fadeInMs) return smoothStep(localAgeMs / fadeInMs);
|
||||
return 1.0f - smoothStep((localAgeMs - fadeInMs) / (lifetimeMs - fadeInMs));
|
||||
};
|
||||
|
||||
GLboolean depthWriteWasEnabled = GL_TRUE;
|
||||
glGetBooleanv(GL_DEPTH_WRITEMASK, &depthWriteWasEnabled);
|
||||
const GLboolean blendWasEnabled = glIsEnabled(GL_BLEND);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
renderer.shaderManager.PushShader("path_visualization");
|
||||
renderer.SetMatrix();
|
||||
constexpr float kMaxOpacity = 0.45f;
|
||||
const float green[3] = { 0.08f, 1.0f, 0.25f };
|
||||
|
||||
for (const Dot& dot : dots) {
|
||||
const float localAge = elapsedMs - dot.delayMs;
|
||||
const float alpha = dotAlpha(localAge, dot.lifetimeMs) * kMaxOpacity;
|
||||
if (alpha <= 0.001f) continue;
|
||||
|
||||
const float scale = 0.65f + 0.35f * smoothStep(localAge / 55.0f);
|
||||
const float color[4] = { green[0], green[1], green[2], alpha };
|
||||
renderer.RenderUniform4fv("uColor", color);
|
||||
renderer.PushMatrix();
|
||||
renderer.TranslateMatrix(dot.position + Eigen::Vector3f(0.0f, 0.025f, 0.0f));
|
||||
renderer.ScaleMatrix(scale);
|
||||
renderer.DrawVertexRenderStruct(dotMesh);
|
||||
renderer.PopMatrix();
|
||||
}
|
||||
|
||||
constexpr float kMarkerFadeInMs = 70.0f;
|
||||
constexpr float kMarkerFadeOutMs = 300.0f;
|
||||
float markerAlpha = smoothStep(elapsedMs / kMarkerFadeInMs);
|
||||
if (elapsedMs > durationMs - kMarkerFadeOutMs) {
|
||||
markerAlpha *= 1.0f - smoothStep(
|
||||
(elapsedMs - (durationMs - kMarkerFadeOutMs)) / kMarkerFadeOutMs);
|
||||
}
|
||||
if (markerAlpha > 0.001f) {
|
||||
const float markerScale = 0.72f + 0.28f * smoothStep(elapsedMs / kMarkerFadeInMs);
|
||||
const float color[4] = { green[0], green[1], green[2], markerAlpha * kMaxOpacity };
|
||||
renderer.RenderUniform4fv("uColor", color);
|
||||
renderer.PushMatrix();
|
||||
renderer.TranslateMatrix(destination + Eigen::Vector3f(0.0f, 0.05f, 0.0f));
|
||||
renderer.ScaleMatrix(markerScale);
|
||||
renderer.DrawVertexRenderStruct(destinationMesh);
|
||||
renderer.PopMatrix();
|
||||
}
|
||||
|
||||
renderer.shaderManager.PopShader();
|
||||
glDepthMask(depthWriteWasEnabled);
|
||||
if (!blendWasEnabled) glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
} // namespace FRG
|
||||
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "render/Renderer.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace FRG {
|
||||
|
||||
class Character;
|
||||
|
||||
class PathVisualization
|
||||
{
|
||||
public:
|
||||
void initialize();
|
||||
void setEnabled(bool enabled);
|
||||
bool isEnabled() const { return enabled; }
|
||||
|
||||
void start(const Character& player, const Eigen::Vector3f& clickedDestination);
|
||||
void cancel();
|
||||
void update(int64_t deltaMs);
|
||||
void draw(Renderer& renderer);
|
||||
|
||||
private:
|
||||
struct Dot {
|
||||
Eigen::Vector3f position = Eigen::Vector3f::Zero();
|
||||
float delayMs = 0.0f;
|
||||
float lifetimeMs = 0.0f;
|
||||
};
|
||||
|
||||
bool enabled = true;
|
||||
bool active = false;
|
||||
float elapsedMs = 0.0f;
|
||||
float durationMs = 0.0f;
|
||||
Eigen::Vector3f destination = Eigen::Vector3f::Zero();
|
||||
std::vector<Dot> dots;
|
||||
VertexRenderStruct dotMesh;
|
||||
VertexRenderStruct destinationMesh;
|
||||
};
|
||||
|
||||
} // namespace FRG
|
||||
Reference in new issue
Block a user