mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-13 21:26:40 +02:00
parabola/polyvox, fixing some bugs
This commit is contained in:
parent
763e6465a2
commit
318ef907b8
23 changed files with 257 additions and 177 deletions
|
@ -618,6 +618,8 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic
|
|||
result.intersects = true;
|
||||
result.avatarID = avatar->getID();
|
||||
result.distance = distance;
|
||||
result.face = face;
|
||||
result.surfaceNormal = surfaceNormal;
|
||||
result.extraInfo = extraInfo;
|
||||
}
|
||||
}
|
||||
|
@ -688,6 +690,8 @@ ParabolaToAvatarIntersectionResult AvatarManager::findParabolaIntersectionVector
|
|||
result.intersects = true;
|
||||
result.avatarID = avatar->getID();
|
||||
result.parabolicDistance = parabolicDistance;
|
||||
result.face = face;
|
||||
result.surfaceNormal = surfaceNormal;
|
||||
result.extraInfo = extraInfo;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ PickResultPointer ParabolaPick::getOverlayIntersection(const PickParabola& pick)
|
|||
PickResultPointer ParabolaPick::getAvatarIntersection(const PickParabola& pick) {
|
||||
ParabolaToAvatarIntersectionResult avatarRes = DependencyManager::get<AvatarManager>()->findParabolaIntersectionVector(pick, getIncludeItemsAs<EntityItemID>(), getIgnoreItemsAs<EntityItemID>());
|
||||
if (avatarRes.intersects) {
|
||||
return std::make_shared<ParabolaPickResult>(IntersectionType::AVATAR, avatarRes.avatarID, avatarRes.distance, avatarRes.parabolicDistance, avatarRes.intersection, pick, glm::vec3(NAN), avatarRes.extraInfo);
|
||||
return std::make_shared<ParabolaPickResult>(IntersectionType::AVATAR, avatarRes.avatarID, avatarRes.distance, avatarRes.parabolicDistance, avatarRes.intersection, pick, avatarRes.surfaceNormal, avatarRes.extraInfo);
|
||||
} else {
|
||||
return std::make_shared<ParabolaPickResult>(pick.toVariantMap());
|
||||
}
|
||||
|
|
|
@ -156,7 +156,6 @@ void ParabolaPointer::RenderState::editParabola(const glm::vec3& color, float al
|
|||
item.setIsVisibleInSecondaryCamera(isVisibleInSecondaryCamera);
|
||||
item.setEnabled(enabled);
|
||||
item.updateKey();
|
||||
item.updateUniformBuffer();
|
||||
});
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
|
@ -182,7 +181,6 @@ void ParabolaPointer::RenderState::update(const glm::vec3& origin, const glm::ve
|
|||
item.setAcceleration(acceleration);
|
||||
item.setParabolicDistance(parabolicDistance);
|
||||
item.setWidth(width);
|
||||
item.updateUniformBuffer();
|
||||
});
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
|
@ -293,7 +291,6 @@ ParabolaPointer::RenderState::ParabolaRenderItem::ParabolaRenderItem(const glm::
|
|||
setAlpha(alpha);
|
||||
setWidth(width);
|
||||
updateKey();
|
||||
updateUniformBuffer();
|
||||
}
|
||||
|
||||
void ParabolaPointer::RenderState::ParabolaRenderItem::setVisible(bool visible) {
|
||||
|
@ -302,12 +299,16 @@ void ParabolaPointer::RenderState::ParabolaRenderItem::setVisible(bool visible)
|
|||
} else {
|
||||
_key = render::ItemKey::Builder(_key).withInvisible();
|
||||
}
|
||||
_visible = visible;
|
||||
}
|
||||
|
||||
void ParabolaPointer::RenderState::ParabolaRenderItem::updateKey() {
|
||||
auto builder = _parabolaData.color.a < 1.0f ? render::ItemKey::Builder::transparentShape() : render::ItemKey::Builder::opaqueShape();
|
||||
// FIXME: There's no way to designate a render item as non-shadow-reciever, and since a parabola's bounding box covers the entire domain,
|
||||
// it seems to block all shadows. I think this is a bug with shadows.
|
||||
//auto builder = _parabolaData.color.a < 1.0f ? render::ItemKey::Builder::transparentShape() : render::ItemKey::Builder::opaqueShape();
|
||||
auto builder = render::ItemKey::Builder::transparentShape();
|
||||
|
||||
if (_enabled) {
|
||||
if (_enabled && _visible) {
|
||||
builder.withVisible();
|
||||
} else {
|
||||
builder.withInvisible();
|
||||
|
@ -358,6 +359,10 @@ const gpu::PipelinePointer ParabolaPointer::RenderState::ParabolaRenderItem::get
|
|||
}
|
||||
|
||||
void ParabolaPointer::RenderState::ParabolaRenderItem::render(RenderArgs* args) {
|
||||
if (!_visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpu::Batch& batch = *(args->_batch);
|
||||
|
||||
Transform transform;
|
||||
|
@ -366,12 +371,17 @@ void ParabolaPointer::RenderState::ParabolaRenderItem::render(RenderArgs* args)
|
|||
|
||||
batch.setPipeline(getParabolaPipeline());
|
||||
|
||||
const int MAX_SECTIONS = 100;
|
||||
if (glm::length2(_parabolaData.acceleration) < EPSILON) {
|
||||
_parabolaData.numSections = 1;
|
||||
} else {
|
||||
_parabolaData.numSections = glm::clamp((int)(_parabolaData.parabolicDistance + 1) * 10, 1, MAX_SECTIONS);
|
||||
}
|
||||
updateUniformBuffer();
|
||||
batch.setUniformBuffer(0, _uniformBuffer);
|
||||
|
||||
// TODO: variable number of sections, depending on ? (acceleration?, parabolicDistance?)
|
||||
const int NUM_SECTIONS = 25; // must match value in parabola.slv
|
||||
// We draw 2 * n + 2 vertices for a triangle strip
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 2 * NUM_SECTIONS + 2, 0);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 2 * _parabolaData.numSections + 2, 0);
|
||||
}
|
||||
|
||||
namespace render {
|
||||
|
|
|
@ -57,6 +57,7 @@ public:
|
|||
|
||||
glm::vec3 _origin { 0.0f };
|
||||
bool _isVisibleInSecondaryCamera { DEFAULT_PARABOLA_ISVISIBLEINSECONDARYCAMERA };
|
||||
bool _visible { false };
|
||||
bool _enabled { false };
|
||||
|
||||
struct ParabolaData {
|
||||
|
@ -65,6 +66,8 @@ public:
|
|||
vec3 acceleration { 0.0f };
|
||||
float width { DEFAULT_PARABOLA_WIDTH };
|
||||
vec4 color { vec4(DEFAULT_PARABOLA_COLOR)};
|
||||
int numSections { 0 };
|
||||
ivec3 spare;
|
||||
};
|
||||
|
||||
ParabolaData _parabolaData;
|
||||
|
|
|
@ -113,13 +113,12 @@ PickResultPointer PathPointer::getVisualPickResult(const PickResultPointer& pick
|
|||
const glm::vec3 DEFAULT_REGISTRATION_POINT = glm::vec3(0.5f);
|
||||
endVec = pos + rot * (dim * (DEFAULT_REGISTRATION_POINT - registrationPoint));
|
||||
glm::vec3 direction = endVec - origin;
|
||||
float distance = glm::distance(origin, endVec);
|
||||
distance = glm::distance(origin, endVec);
|
||||
glm::vec3 normalizedDirection = glm::normalize(direction);
|
||||
|
||||
type = _lockEndObject.isOverlay ? IntersectionType::OVERLAY : IntersectionType::ENTITY;
|
||||
id = _lockEndObject.id;
|
||||
intersection = endVec;
|
||||
distance = distance;
|
||||
surfaceNormal = -normalizedDirection;
|
||||
setVisualPickResultInternal(visualPickResult, type, id, intersection, distance, surfaceNormal);
|
||||
} else if (type != IntersectionType::NONE && _lockEnd) {
|
||||
|
@ -134,7 +133,6 @@ PickResultPointer PathPointer::getVisualPickResult(const PickResultPointer& pick
|
|||
glm::vec3 direction = endVec - origin;
|
||||
distance = glm::distance(origin, endVec);
|
||||
glm::vec3 normalizedDirection = glm::normalize(direction);
|
||||
type = type;
|
||||
intersection = endVec;
|
||||
surfaceNormal = -normalizedDirection;
|
||||
setVisualPickResultInternal(visualPickResult, type, id, intersection, distance, surfaceNormal);
|
||||
|
|
|
@ -79,11 +79,11 @@ public:
|
|||
void updateVisuals(const PickResultPointer& prevRayPickResult) override;
|
||||
|
||||
protected:
|
||||
PointerTriggers _triggers;
|
||||
float _pathLength { 0.0f };
|
||||
RenderStateMap _renderStates;
|
||||
DefaultRenderStateMap _defaultRenderStates;
|
||||
std::string _currentRenderState { "" };
|
||||
PointerTriggers _triggers;
|
||||
float _pathLength { 0.0f };
|
||||
bool _faceAvatar;
|
||||
bool _followNormal;
|
||||
bool _centerEndY;
|
||||
|
|
|
@ -39,7 +39,7 @@ PickResultPointer RayPick::getOverlayIntersection(const PickRay& pick) {
|
|||
PickResultPointer RayPick::getAvatarIntersection(const PickRay& pick) {
|
||||
RayToAvatarIntersectionResult avatarRes = DependencyManager::get<AvatarManager>()->findRayIntersectionVector(pick, getIncludeItemsAs<EntityItemID>(), getIgnoreItemsAs<EntityItemID>());
|
||||
if (avatarRes.intersects) {
|
||||
return std::make_shared<RayPickResult>(IntersectionType::AVATAR, avatarRes.avatarID, avatarRes.distance, avatarRes.intersection, pick, glm::vec3(NAN), avatarRes.extraInfo);
|
||||
return std::make_shared<RayPickResult>(IntersectionType::AVATAR, avatarRes.avatarID, avatarRes.distance, avatarRes.intersection, pick, avatarRes.surfaceNormal, avatarRes.extraInfo);
|
||||
} else {
|
||||
return std::make_shared<RayPickResult>(pick.toVariantMap());
|
||||
}
|
||||
|
|
|
@ -622,75 +622,37 @@ ParabolaToOverlayIntersectionResult Overlays::findParabolaIntersectionVector(con
|
|||
}
|
||||
|
||||
QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine, const RayToOverlayIntersectionResult& value) {
|
||||
auto obj = engine->newObject();
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("intersects", value.intersects);
|
||||
obj.setProperty("overlayID", OverlayIDtoScriptValue(engine, value.overlayID));
|
||||
QScriptValue overlayIDValue = quuidToScriptValue(engine, value.overlayID);
|
||||
obj.setProperty("overlayID", overlayIDValue);
|
||||
obj.setProperty("distance", value.distance);
|
||||
obj.setProperty("face", boxFaceToString(value.face));
|
||||
|
||||
QString faceName = "";
|
||||
// handle BoxFace
|
||||
switch (value.face) {
|
||||
case MIN_X_FACE:
|
||||
faceName = "MIN_X_FACE";
|
||||
break;
|
||||
case MAX_X_FACE:
|
||||
faceName = "MAX_X_FACE";
|
||||
break;
|
||||
case MIN_Y_FACE:
|
||||
faceName = "MIN_Y_FACE";
|
||||
break;
|
||||
case MAX_Y_FACE:
|
||||
faceName = "MAX_Y_FACE";
|
||||
break;
|
||||
case MIN_Z_FACE:
|
||||
faceName = "MIN_Z_FACE";
|
||||
break;
|
||||
case MAX_Z_FACE:
|
||||
faceName = "MAX_Z_FACE";
|
||||
break;
|
||||
default:
|
||||
case UNKNOWN_FACE:
|
||||
faceName = "UNKNOWN_FACE";
|
||||
break;
|
||||
}
|
||||
obj.setProperty("face", faceName);
|
||||
auto intersection = vec3toScriptValue(engine, value.intersection);
|
||||
QScriptValue intersection = vec3toScriptValue(engine, value.intersection);
|
||||
obj.setProperty("intersection", intersection);
|
||||
QScriptValue surfaceNormal = vec3toScriptValue(engine, value.surfaceNormal);
|
||||
obj.setProperty("surfaceNormal", surfaceNormal);
|
||||
obj.setProperty("extraInfo", engine->toScriptValue(value.extraInfo));
|
||||
return obj;
|
||||
}
|
||||
|
||||
void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& objectVar, RayToOverlayIntersectionResult& value) {
|
||||
QVariantMap object = objectVar.toVariant().toMap();
|
||||
value.intersects = object["intersects"].toBool();
|
||||
value.overlayID = OverlayID(QUuid(object["overlayID"].toString()));
|
||||
value.distance = object["distance"].toFloat();
|
||||
void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, RayToOverlayIntersectionResult& value) {
|
||||
value.intersects = object.property("intersects").toVariant().toBool();
|
||||
QScriptValue overlayIDValue = object.property("overlayID");
|
||||
quuidFromScriptValue(overlayIDValue, value.overlayID);
|
||||
value.distance = object.property("distance").toVariant().toFloat();
|
||||
value.face = boxFaceFromString(object.property("face").toVariant().toString());
|
||||
|
||||
QString faceName = object["face"].toString();
|
||||
if (faceName == "MIN_X_FACE") {
|
||||
value.face = MIN_X_FACE;
|
||||
} else if (faceName == "MAX_X_FACE") {
|
||||
value.face = MAX_X_FACE;
|
||||
} else if (faceName == "MIN_Y_FACE") {
|
||||
value.face = MIN_Y_FACE;
|
||||
} else if (faceName == "MAX_Y_FACE") {
|
||||
value.face = MAX_Y_FACE;
|
||||
} else if (faceName == "MIN_Z_FACE") {
|
||||
value.face = MIN_Z_FACE;
|
||||
} else if (faceName == "MAX_Z_FACE") {
|
||||
value.face = MAX_Z_FACE;
|
||||
} else {
|
||||
value.face = UNKNOWN_FACE;
|
||||
};
|
||||
auto intersection = object["intersection"];
|
||||
QScriptValue intersection = object.property("intersection");
|
||||
if (intersection.isValid()) {
|
||||
bool valid;
|
||||
auto newIntersection = vec3FromVariant(intersection, valid);
|
||||
if (valid) {
|
||||
value.intersection = newIntersection;
|
||||
}
|
||||
vec3FromScriptValue(intersection, value.intersection);
|
||||
}
|
||||
value.extraInfo = object["extraInfo"].toMap();
|
||||
QScriptValue surfaceNormal = object.property("surfaceNormal");
|
||||
if (surfaceNormal.isValid()) {
|
||||
vec3FromScriptValue(surfaceNormal, value.surfaceNormal);
|
||||
}
|
||||
value.extraInfo = object.property("extraInfo").toVariant().toMap();
|
||||
}
|
||||
|
||||
bool Overlays::isLoaded(OverlayID id) {
|
||||
|
|
|
@ -59,7 +59,7 @@ class RayToOverlayIntersectionResult {
|
|||
public:
|
||||
bool intersects { false };
|
||||
OverlayID overlayID { UNKNOWN_OVERLAY_ID };
|
||||
float distance { 0 };
|
||||
float distance { 0.0f };
|
||||
BoxFace face { UNKNOWN_FACE };
|
||||
glm::vec3 surfaceNormal;
|
||||
glm::vec3 intersection;
|
||||
|
@ -73,8 +73,8 @@ class ParabolaToOverlayIntersectionResult {
|
|||
public:
|
||||
bool intersects { false };
|
||||
OverlayID overlayID { UNKNOWN_OVERLAY_ID };
|
||||
float distance { 0 };
|
||||
float parabolicDistance { 0 };
|
||||
float distance { 0.0f };
|
||||
float parabolicDistance { 0.0f };
|
||||
BoxFace face { UNKNOWN_FACE };
|
||||
glm::vec3 surfaceNormal;
|
||||
glm::vec3 intersection;
|
||||
|
|
|
@ -2555,15 +2555,18 @@ glm::mat4 AvatarData::getControllerRightHandMatrix() const {
|
|||
return _controllerRightHandMatrixCache.get();
|
||||
}
|
||||
|
||||
|
||||
QScriptValue RayToAvatarIntersectionResultToScriptValue(QScriptEngine* engine, const RayToAvatarIntersectionResult& value) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("intersects", value.intersects);
|
||||
QScriptValue avatarIDValue = quuidToScriptValue(engine, value.avatarID);
|
||||
obj.setProperty("avatarID", avatarIDValue);
|
||||
obj.setProperty("distance", value.distance);
|
||||
obj.setProperty("face", boxFaceToString(value.face));
|
||||
|
||||
QScriptValue intersection = vec3toScriptValue(engine, value.intersection);
|
||||
obj.setProperty("intersection", intersection);
|
||||
QScriptValue surfaceNormal = vec3toScriptValue(engine, value.surfaceNormal);
|
||||
obj.setProperty("surfaceNormal", surfaceNormal);
|
||||
obj.setProperty("extraInfo", engine->toScriptValue(value.extraInfo));
|
||||
return obj;
|
||||
}
|
||||
|
@ -2573,10 +2576,16 @@ void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, Ra
|
|||
QScriptValue avatarIDValue = object.property("avatarID");
|
||||
quuidFromScriptValue(avatarIDValue, value.avatarID);
|
||||
value.distance = object.property("distance").toVariant().toFloat();
|
||||
value.face = boxFaceFromString(object.property("face").toVariant().toString());
|
||||
|
||||
QScriptValue intersection = object.property("intersection");
|
||||
if (intersection.isValid()) {
|
||||
vec3FromScriptValue(intersection, value.intersection);
|
||||
}
|
||||
QScriptValue surfaceNormal = object.property("surfaceNormal");
|
||||
if (surfaceNormal.isValid()) {
|
||||
vec3FromScriptValue(surfaceNormal, value.surfaceNormal);
|
||||
}
|
||||
value.extraInfo = object.property("extraInfo").toVariant().toMap();
|
||||
}
|
||||
|
||||
|
|
|
@ -1527,7 +1527,9 @@ public:
|
|||
bool intersects { false };
|
||||
QUuid avatarID;
|
||||
float distance { 0.0f };
|
||||
BoxFace face;
|
||||
glm::vec3 intersection;
|
||||
glm::vec3 surfaceNormal;
|
||||
QVariantMap extraInfo;
|
||||
};
|
||||
Q_DECLARE_METATYPE(RayToAvatarIntersectionResult)
|
||||
|
@ -1540,7 +1542,9 @@ public:
|
|||
QUuid avatarID;
|
||||
float distance { 0.0f };
|
||||
float parabolicDistance { 0.0f };
|
||||
BoxFace face;
|
||||
glm::vec3 intersection;
|
||||
glm::vec3 surfaceNormal;
|
||||
QVariantMap extraInfo;
|
||||
};
|
||||
|
||||
|
|
|
@ -567,8 +567,7 @@ public:
|
|||
bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
OctreeElementPointer& element,
|
||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const
|
||||
{
|
||||
QVariantMap& extraInfo, bool precisionPicking) const {
|
||||
// TODO -- correctly pick against marching-cube generated meshes
|
||||
if (!precisionPicking) {
|
||||
// just intersect with bounding box
|
||||
|
@ -614,6 +613,87 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o
|
|||
return hit;
|
||||
}
|
||||
|
||||
bool RenderablePolyVoxEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
|
||||
const glm::vec3& acceleration, OctreeElementPointer& element,
|
||||
float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const {
|
||||
// TODO -- correctly pick against marching-cube generated meshes
|
||||
if (!precisionPicking) {
|
||||
// just intersect with bounding box
|
||||
return true;
|
||||
}
|
||||
|
||||
glm::mat4 wtvMatrix = worldToVoxelMatrix();
|
||||
glm::vec4 originInVoxel = wtvMatrix * glm::vec4(origin, 1.0f);
|
||||
glm::vec4 velocityInVoxel = wtvMatrix * glm::vec4(velocity, 0.0f);
|
||||
glm::vec4 accelerationInVoxel = wtvMatrix * glm::vec4(acceleration, 0.0f);
|
||||
|
||||
// find the first intersection with the voxel bounding box (slightly enlarged so we can catch voxels that touch the sides)
|
||||
bool success;
|
||||
glm::vec3 center = getCenterPosition(success);
|
||||
glm::vec3 dimensions = getScaledDimensions();
|
||||
const float FIRST_BOX_HALF_SCALE = 0.51f;
|
||||
AABox voxelBox1(wtvMatrix * vec4(center - FIRST_BOX_HALF_SCALE * dimensions, 1.0f),
|
||||
wtvMatrix * vec4(2.0f * FIRST_BOX_HALF_SCALE * dimensions, 0.0f));
|
||||
bool hit1;
|
||||
float parabolicDistance1;
|
||||
// If we're starting inside the box, our first point is originInVoxel
|
||||
if (voxelBox1.contains(originInVoxel)) {
|
||||
parabolicDistance1 = 0.0f;
|
||||
hit1 = true;
|
||||
} else {
|
||||
BoxFace face1;
|
||||
glm::vec3 surfaceNormal1;
|
||||
hit1 = voxelBox1.findParabolaIntersection(glm::vec3(originInVoxel), glm::vec3(velocityInVoxel), glm::vec3(accelerationInVoxel),
|
||||
parabolicDistance1, face1, surfaceNormal1);
|
||||
}
|
||||
|
||||
if (hit1) {
|
||||
// find the second intersection, which should be with the inside of the box (use a slightly large box again)
|
||||
const float SECOND_BOX_HALF_SCALE = 0.52f;
|
||||
AABox voxelBox2(wtvMatrix * vec4(center - SECOND_BOX_HALF_SCALE * dimensions, 1.0f),
|
||||
wtvMatrix * vec4(2.0f * SECOND_BOX_HALF_SCALE * dimensions, 0.0f));
|
||||
glm::vec4 originInVoxel2 = originInVoxel + velocityInVoxel * parabolicDistance1 + 0.5f * accelerationInVoxel * parabolicDistance1 * parabolicDistance1;
|
||||
glm::vec4 velocityInVoxel2 = velocityInVoxel + accelerationInVoxel * parabolicDistance1;
|
||||
glm::vec4 accelerationInVoxel2 = accelerationInVoxel;
|
||||
float parabolicDistance2;
|
||||
BoxFace face2;
|
||||
glm::vec3 surfaceNormal2;
|
||||
// this should always be true
|
||||
if (voxelBox2.findParabolaIntersection(glm::vec3(originInVoxel2), glm::vec3(velocityInVoxel2), glm::vec3(accelerationInVoxel2),
|
||||
parabolicDistance2, face2, surfaceNormal2)) {
|
||||
const int MAX_SECTIONS = 15;
|
||||
PolyVox::RaycastResult raycastResult = PolyVox::RaycastResults::Completed;
|
||||
glm::vec4 result = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glm::vec4 segmentStartVoxel = originInVoxel2;
|
||||
for (int i = 0; i < MAX_SECTIONS; i++) {
|
||||
float t = parabolicDistance2 * ((float)(i + 1)) / ((float)MAX_SECTIONS);
|
||||
glm::vec4 segmentEndVoxel = originInVoxel2 + velocityInVoxel2 * t + 0.5f * accelerationInVoxel2 * t * t;
|
||||
raycastResult = doRayCast(segmentStartVoxel, segmentEndVoxel, result);
|
||||
if (raycastResult != PolyVox::RaycastResults::Completed) {
|
||||
// We hit something!
|
||||
break;
|
||||
}
|
||||
segmentStartVoxel = segmentEndVoxel;
|
||||
}
|
||||
|
||||
if (raycastResult == PolyVox::RaycastResults::Completed) {
|
||||
// the parabola completed its path -- nothing was hit.
|
||||
return false;
|
||||
}
|
||||
|
||||
glm::vec3 result3 = glm::vec3(result);
|
||||
|
||||
AABox voxelBox;
|
||||
voxelBox += result3 - Vectors::HALF;
|
||||
voxelBox += result3 + Vectors::HALF;
|
||||
|
||||
return voxelBox.findParabolaIntersection(glm::vec3(originInVoxel), glm::vec3(velocityInVoxel), glm::vec3(accelerationInVoxel),
|
||||
parabolicDistance, face, surfaceNormal);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
PolyVox::RaycastResult RenderablePolyVoxEntityItem::doRayCast(glm::vec4 originInVoxel,
|
||||
glm::vec4 farInVoxel,
|
||||
|
|
|
@ -53,9 +53,13 @@ public:
|
|||
|
||||
virtual bool supportsDetailedIntersection() const override { return true; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const override;
|
||||
OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const override;
|
||||
virtual bool findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const vec3& accleration,
|
||||
OctreeElementPointer& element, float& parabolicDistance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const override;
|
||||
|
||||
virtual void setVoxelData(const QByteArray& voxelData) override;
|
||||
virtual void setVoxelVolumeSize(const glm::vec3& voxelVolumeSize) override;
|
||||
|
|
|
@ -1050,65 +1050,16 @@ bool EntityScriptingInterface::getDrawZoneBoundaries() const {
|
|||
}
|
||||
|
||||
QScriptValue RayToEntityIntersectionResultToScriptValue(QScriptEngine* engine, const RayToEntityIntersectionResult& value) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("intersects", value.intersects);
|
||||
obj.setProperty("accurate", value.accurate);
|
||||
QScriptValue entityItemValue = EntityItemIDtoScriptValue(engine, value.entityID);
|
||||
obj.setProperty("entityID", entityItemValue);
|
||||
|
||||
obj.setProperty("distance", value.distance);
|
||||
|
||||
QString faceName = "";
|
||||
// handle BoxFace
|
||||
/**jsdoc
|
||||
* <p>A <code>BoxFace</code> specifies the face of an axis-aligned (AA) box.
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Value</th><th>Description</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>"MIN_X_FACE"</code></td><td>The minimum x-axis face.</td></tr>
|
||||
* <tr><td><code>"MAX_X_FACE"</code></td><td>The maximum x-axis face.</td></tr>
|
||||
* <tr><td><code>"MIN_Y_FACE"</code></td><td>The minimum y-axis face.</td></tr>
|
||||
* <tr><td><code>"MAX_Y_FACE"</code></td><td>The maximum y-axis face.</td></tr>
|
||||
* <tr><td><code>"MIN_Z_FACE"</code></td><td>The minimum z-axis face.</td></tr>
|
||||
* <tr><td><code>"MAX_Z_FACE"</code></td><td>The maximum z-axis face.</td></tr>
|
||||
* <tr><td><code>"UNKNOWN_FACE"</code></td><td>Unknown value.</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef {string} BoxFace
|
||||
*/
|
||||
// FIXME: Move enum to string function to BoxBase.cpp.
|
||||
switch (value.face) {
|
||||
case MIN_X_FACE:
|
||||
faceName = "MIN_X_FACE";
|
||||
break;
|
||||
case MAX_X_FACE:
|
||||
faceName = "MAX_X_FACE";
|
||||
break;
|
||||
case MIN_Y_FACE:
|
||||
faceName = "MIN_Y_FACE";
|
||||
break;
|
||||
case MAX_Y_FACE:
|
||||
faceName = "MAX_Y_FACE";
|
||||
break;
|
||||
case MIN_Z_FACE:
|
||||
faceName = "MIN_Z_FACE";
|
||||
break;
|
||||
case MAX_Z_FACE:
|
||||
faceName = "MAX_Z_FACE";
|
||||
break;
|
||||
case UNKNOWN_FACE:
|
||||
faceName = "UNKNOWN_FACE";
|
||||
break;
|
||||
}
|
||||
obj.setProperty("face", faceName);
|
||||
obj.setProperty("face", boxFaceToString(value.face));
|
||||
|
||||
QScriptValue intersection = vec3toScriptValue(engine, value.intersection);
|
||||
obj.setProperty("intersection", intersection);
|
||||
|
||||
QScriptValue surfaceNormal = vec3toScriptValue(engine, value.surfaceNormal);
|
||||
obj.setProperty("surfaceNormal", surfaceNormal);
|
||||
obj.setProperty("extraInfo", engine->toScriptValue(value.extraInfo));
|
||||
|
@ -1116,29 +1067,13 @@ QScriptValue RayToEntityIntersectionResultToScriptValue(QScriptEngine* engine, c
|
|||
}
|
||||
|
||||
void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, RayToEntityIntersectionResult& value) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
value.intersects = object.property("intersects").toVariant().toBool();
|
||||
value.accurate = object.property("accurate").toVariant().toBool();
|
||||
QScriptValue entityIDValue = object.property("entityID");
|
||||
// EntityItemIDfromScriptValue(entityIDValue, value.entityID);
|
||||
quuidFromScriptValue(entityIDValue, value.entityID);
|
||||
value.distance = object.property("distance").toVariant().toFloat();
|
||||
value.face = boxFaceFromString(object.property("face").toVariant().toString());
|
||||
|
||||
QString faceName = object.property("face").toVariant().toString();
|
||||
if (faceName == "MIN_X_FACE") {
|
||||
value.face = MIN_X_FACE;
|
||||
} else if (faceName == "MAX_X_FACE") {
|
||||
value.face = MAX_X_FACE;
|
||||
} else if (faceName == "MIN_Y_FACE") {
|
||||
value.face = MIN_Y_FACE;
|
||||
} else if (faceName == "MAX_Y_FACE") {
|
||||
value.face = MAX_Y_FACE;
|
||||
} else if (faceName == "MIN_Z_FACE") {
|
||||
value.face = MIN_Z_FACE;
|
||||
} else {
|
||||
value.face = MAX_Z_FACE;
|
||||
};
|
||||
QScriptValue intersection = object.property("intersection");
|
||||
if (intersection.isValid()) {
|
||||
vec3FromScriptValue(intersection, value.intersection);
|
||||
|
|
|
@ -80,9 +80,7 @@ public:
|
|||
glm::vec3 surfaceNormal;
|
||||
QVariantMap extraInfo;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(RayToEntityIntersectionResult)
|
||||
|
||||
QScriptValue RayToEntityIntersectionResultToScriptValue(QScriptEngine* engine, const RayToEntityIntersectionResult& results);
|
||||
void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, RayToEntityIntersectionResult& results);
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ class PolyLineEntityItem : public EntityItem {
|
|||
virtual bool findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
|
||||
const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const { return false; }
|
||||
QVariantMap& extraInfo, bool precisionPicking) const override { return false; }
|
||||
|
||||
// disable these external interfaces as PolyLineEntities caculate their own dimensions based on the points they contain
|
||||
virtual void setRegistrationPoint(const glm::vec3& value) override {}; // FIXME: this is suspicious!
|
||||
|
|
|
@ -50,7 +50,7 @@ class PolyVoxEntityItem : public EntityItem {
|
|||
virtual bool findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
|
||||
const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const { return false; }
|
||||
QVariantMap& extraInfo, bool precisionPicking) const override { return false; }
|
||||
|
||||
virtual void debugDump() const override;
|
||||
|
||||
|
|
|
@ -516,9 +516,9 @@ bool Model::findParabolaIntersectionAgainstSubMeshes(const glm::vec3& origin, co
|
|||
face = triangleSetFace;
|
||||
bestModelTriangle = triangleSetTriangle;
|
||||
bestWorldTriangle = triangleSetTriangle * meshToWorldMatrix;
|
||||
glm::vec3 worldIntersectionPoint = meshFrameOrigin + meshFrameVelocity * triangleSetDistance +
|
||||
glm::vec3 meshIntersectionPoint = meshFrameOrigin + meshFrameVelocity * triangleSetDistance +
|
||||
0.5f * meshFrameAcceleration * triangleSetDistance * triangleSetDistance;
|
||||
glm::vec3 meshIntersectionPoint = origin + velocity * triangleSetDistance +
|
||||
glm::vec3 worldIntersectionPoint = origin + velocity * triangleSetDistance +
|
||||
0.5f * acceleration * triangleSetDistance * triangleSetDistance;
|
||||
extraInfo["worldIntersectionPoint"] = vec3toVariant(worldIntersectionPoint);
|
||||
extraInfo["meshIntersectionPoint"] = vec3toVariant(meshIntersectionPoint);
|
||||
|
|
|
@ -18,6 +18,8 @@ layout(std140) uniform parabolaData {
|
|||
vec3 acceleration;
|
||||
float width;
|
||||
vec4 color;
|
||||
int numSections;
|
||||
ivec3 spare;
|
||||
};
|
||||
|
||||
out vec4 _color;
|
||||
|
@ -25,8 +27,7 @@ out vec4 _color;
|
|||
void main(void) {
|
||||
_color = color;
|
||||
|
||||
const int NUM_SECTIONS = 25; // must match value in ParabolaPointer.cpp
|
||||
float t = parabolicDistance * (floor(gl_VertexID / 2) / float(NUM_SECTIONS));
|
||||
float t = parabolicDistance * (floor(gl_VertexID / 2) / float(numSections));
|
||||
|
||||
vec4 pos = vec4(velocity * t + 0.5 * acceleration * t * t, 1);
|
||||
const float EPSILON = 0.00001;
|
||||
|
|
46
libraries/shared/src/BoxBase.cpp
Normal file
46
libraries/shared/src/BoxBase.cpp
Normal file
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// Created by Sam Gondelman on 7/20/18
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "BoxBase.h"
|
||||
|
||||
QString boxFaceToString(BoxFace face) {
|
||||
switch (face) {
|
||||
case MIN_X_FACE:
|
||||
return "MIN_X_FACE";
|
||||
case MAX_X_FACE:
|
||||
return "MAX_X_FACE";
|
||||
case MIN_Y_FACE:
|
||||
return "MIN_Y_FACE";
|
||||
case MAX_Y_FACE:
|
||||
return "MAX_Y_FACE";
|
||||
case MIN_Z_FACE:
|
||||
return "MIN_Z_FACE";
|
||||
case MAX_Z_FACE:
|
||||
return "MAX_Z_FACE";
|
||||
default:
|
||||
return "UNKNOWN_FACE";
|
||||
}
|
||||
}
|
||||
|
||||
BoxFace boxFaceFromString(const QString& face) {
|
||||
if (face == "MIN_X_FACE") {
|
||||
return MIN_X_FACE;
|
||||
} else if (face == "MAX_X_FACE") {
|
||||
return MAX_X_FACE;
|
||||
} else if (face == "MIN_Y_FACE") {
|
||||
return MIN_Y_FACE;
|
||||
} else if (face == "MAX_Y_FACE") {
|
||||
return MAX_Y_FACE;
|
||||
} else if (face == "MIN_Z_FACE") {
|
||||
return MIN_Z_FACE;
|
||||
} else if (face == "MAX_Z_FACE") {
|
||||
return MAX_Z_FACE;
|
||||
} else {
|
||||
return UNKNOWN_FACE;
|
||||
}
|
||||
}
|
|
@ -16,7 +16,26 @@
|
|||
#define hifi_BoxBase_h
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <QString>
|
||||
|
||||
/**jsdoc
|
||||
* <p>A <code>BoxFace</code> specifies the face of an axis-aligned (AA) box.
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Value</th><th>Description</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>"MIN_X_FACE"</code></td><td>The minimum x-axis face.</td></tr>
|
||||
* <tr><td><code>"MAX_X_FACE"</code></td><td>The maximum x-axis face.</td></tr>
|
||||
* <tr><td><code>"MIN_Y_FACE"</code></td><td>The minimum y-axis face.</td></tr>
|
||||
* <tr><td><code>"MAX_Y_FACE"</code></td><td>The maximum y-axis face.</td></tr>
|
||||
* <tr><td><code>"MIN_Z_FACE"</code></td><td>The minimum z-axis face.</td></tr>
|
||||
* <tr><td><code>"MAX_Z_FACE"</code></td><td>The maximum z-axis face.</td></tr>
|
||||
* <tr><td><code>"UNKNOWN_FACE"</code></td><td>Unknown value.</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef {string} BoxFace
|
||||
*/
|
||||
enum BoxFace {
|
||||
MIN_X_FACE,
|
||||
MAX_X_FACE,
|
||||
|
@ -27,6 +46,9 @@ enum BoxFace {
|
|||
UNKNOWN_FACE
|
||||
};
|
||||
|
||||
QString boxFaceToString(BoxFace face);
|
||||
BoxFace boxFaceFromString(const QString& face);
|
||||
|
||||
enum BoxVertex {
|
||||
BOTTOM_LEFT_NEAR = 0,
|
||||
BOTTOM_RIGHT_NEAR = 1,
|
||||
|
|
|
@ -952,7 +952,7 @@ void checkPossibleParabolicIntersectionWithTriangle(float t, float& minDistance,
|
|||
|
||||
bool findParabolaTriangleIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
||||
const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, float& parabolicDistance, bool allowBackface) {
|
||||
glm::vec3 normal = glm::cross(v2 - v1, v0 - v1);
|
||||
glm::vec3 normal = glm::normalize(glm::cross(v2 - v1, v0 - v1));
|
||||
|
||||
// We transform the parabola and triangle so that the triangle is in the plane z = 0, with v0 at the origin
|
||||
glm::quat inverseRot;
|
||||
|
@ -1092,7 +1092,7 @@ inline float parabolaVelocityAtT(float velocity, float acceleration, float t) {
|
|||
bool findParabolaAABoxIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
||||
const glm::vec3& corner, const glm::vec3& scale, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal) {
|
||||
float minDistance = FLT_MAX;
|
||||
BoxFace minFace;
|
||||
BoxFace minFace = UNKNOWN_FACE;
|
||||
glm::vec3 minNormal;
|
||||
glm::vec2 possibleDistances;
|
||||
float a, b, c;
|
||||
|
@ -1720,8 +1720,8 @@ unsigned int solveP3(float* x, float a, float b, float c) {
|
|||
a /= 3.0f;
|
||||
q = -2.0f * sqrtf(q);
|
||||
x[0] = q * cosf(t / 3.0f) - a;
|
||||
x[1] = q * cosf((t + 2.0f * M_PI) / 3.0f) - a;
|
||||
x[2] = q * cosf((t - 2.0f * M_PI) / 3.0f) - a;
|
||||
x[1] = q * cosf((t + 2.0f * (float)M_PI) / 3.0f) - a;
|
||||
x[2] = q * cosf((t - 2.0f * (float)M_PI) / 3.0f) - a;
|
||||
return 3;
|
||||
} else {
|
||||
A = -powf(fabsf(r) + sqrtf(r2 - q3), 1.0f / 3.0f);
|
||||
|
@ -1755,27 +1755,27 @@ bool solve_quartic(float a, float b, float c, float d, glm::vec4& roots) {
|
|||
|
||||
y = px3[0];
|
||||
if (iZeroes != 1) {
|
||||
if (fabs(px3[1]) > fabs(y)) {
|
||||
if (fabsf(px3[1]) > fabsf(y)) {
|
||||
y = px3[1];
|
||||
}
|
||||
if (fabs(px3[2]) > fabs(y)) {
|
||||
if (fabsf(px3[2]) > fabsf(y)) {
|
||||
y = px3[2];
|
||||
}
|
||||
}
|
||||
|
||||
D = y * y - 4.0f * d;
|
||||
if (fabs(D) < EPSILON) {
|
||||
if (fabsf(D) < EPSILON) {
|
||||
q1 = q2 = 0.5f * y;
|
||||
D = a * a - 4.0f * (b - y);
|
||||
if (fabs(D) < EPSILON) {
|
||||
if (fabsf(D) < EPSILON) {
|
||||
p1 = p2 = 0.5f * a;
|
||||
} else {
|
||||
sqD = sqrt(D);
|
||||
sqD = sqrtf(D);
|
||||
p1 = 0.5f * (a + sqD);
|
||||
p2 = 0.5f * (a - sqD);
|
||||
}
|
||||
} else {
|
||||
sqD = sqrt(D);
|
||||
sqD = sqrtf(D);
|
||||
q1 = 0.5f * (y + sqD);
|
||||
q2 = 0.5f * (y - sqD);
|
||||
p1 = (a * q1 - c) / (q1 - q2);
|
||||
|
@ -1786,10 +1786,10 @@ bool solve_quartic(float a, float b, float c, float d, glm::vec4& roots) {
|
|||
D = p1 * p1 - 4.0f * q1;
|
||||
if (D < 0.0f) {
|
||||
x1.real(-0.5f * p1);
|
||||
x1.imag(0.5f * sqrt(-D));
|
||||
x1.imag(0.5f * sqrtf(-D));
|
||||
x2 = std::conj(x1);
|
||||
} else {
|
||||
sqD = sqrt(D);
|
||||
sqD = sqrtf(D);
|
||||
x1.real(0.5f * (-p1 + sqD));
|
||||
x2.real(0.5f * (-p1 - sqD));
|
||||
}
|
||||
|
@ -1797,10 +1797,10 @@ bool solve_quartic(float a, float b, float c, float d, glm::vec4& roots) {
|
|||
D = p2 * p2 - 4.0f * q2;
|
||||
if (D < 0.0f) {
|
||||
x3.real(-0.5f * p2);
|
||||
x3.imag(0.5f * sqrt(-D));
|
||||
x3.imag(0.5f * sqrtf(-D));
|
||||
x4 = std::conj(x3);
|
||||
} else {
|
||||
sqD = sqrt(D);
|
||||
sqD = sqrtf(D);
|
||||
x3.real(0.5f * (-p2 + sqD));
|
||||
x4.real(0.5f * (-p2 - sqD));
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
{name: "teleport", path: teleportPath, end: teleportEnd},
|
||||
{name: "seat", path: seatPath, end: seatEnd}];
|
||||
|
||||
var DEFAULT_DISTANCE = 10;
|
||||
var DEFAULT_DISTANCE = 4.0;
|
||||
var teleportDefaultRenderStates = [{name: "cancel", distance: DEFAULT_DISTANCE, path: cancelPath}];
|
||||
|
||||
var coolInTimeout = null;
|
||||
|
@ -151,7 +151,8 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
accelerationAxis: accelerationAxis,
|
||||
rotateAccelerationWithAvatar: true,
|
||||
renderStates: teleportRenderStates,
|
||||
defaultRenderStates: teleportDefaultRenderStates
|
||||
defaultRenderStates: teleportDefaultRenderStates,
|
||||
maxDistance: 4.0
|
||||
});
|
||||
this.teleportParabolaHandInvisible = Pointers.createPointer(PickType.Parabola, {
|
||||
joint: (_this.hand === RIGHT_HAND) ? "RightHand" : "LeftHand",
|
||||
|
@ -163,7 +164,8 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
speed: speed,
|
||||
accelerationAxis: accelerationAxis,
|
||||
rotateAccelerationWithAvatar: true,
|
||||
renderStates: teleportRenderStates
|
||||
renderStates: teleportRenderStates,
|
||||
maxDistance: 4.0
|
||||
});
|
||||
this.teleportParabolaHeadVisible = Pointers.createPointer(PickType.Parabola, {
|
||||
joint: "Avatar",
|
||||
|
@ -176,7 +178,8 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
accelerationAxis: accelerationAxis,
|
||||
rotateAccelerationWithAvatar: true,
|
||||
renderStates: teleportRenderStates,
|
||||
defaultRenderStates: teleportDefaultRenderStates
|
||||
defaultRenderStates: teleportDefaultRenderStates,
|
||||
maxDistance: 4.0
|
||||
});
|
||||
this.teleportParabolaHeadInvisible = Pointers.createPointer(PickType.Parabola, {
|
||||
joint: "Avatar",
|
||||
|
@ -188,7 +191,8 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
speed: speed,
|
||||
accelerationAxis: accelerationAxis,
|
||||
rotateAccelerationWithAvatar: true,
|
||||
renderStates: teleportRenderStates
|
||||
renderStates: teleportRenderStates,
|
||||
maxDistance: 4.0
|
||||
});
|
||||
|
||||
this.cleanup = function() {
|
||||
|
@ -430,7 +434,7 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
}
|
||||
|
||||
var surfaceNormal = result.surfaceNormal;
|
||||
var angle = Math.abs(Math.acos(Vec3.dot(surfaceNormal, Quat.getUp(MyAvatar.orientation)))) * (180.0 / Math.PI);
|
||||
var angle = Math.acos(Vec3.dot(surfaceNormal, Quat.getUp(MyAvatar.orientation))) * (180.0 / Math.PI);
|
||||
|
||||
if (angle > MAX_ANGLE_FROM_UP_TO_TELEPORT ||
|
||||
Vec3.distance(MyAvatar.position, result.intersection) <= TELEPORT_CANCEL_RANGE * MyAvatar.sensorToWorldScale) {
|
||||
|
|
Loading…
Reference in a new issue