mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 14:29:03 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into punk
This commit is contained in:
commit
0f6f465e7f
16 changed files with 328 additions and 107 deletions
|
@ -5261,6 +5261,7 @@ void Application::resetPhysicsReadyInformation() {
|
||||||
_nearbyEntitiesCountAtLastPhysicsCheck = 0;
|
_nearbyEntitiesCountAtLastPhysicsCheck = 0;
|
||||||
_nearbyEntitiesStabilityCount = 0;
|
_nearbyEntitiesStabilityCount = 0;
|
||||||
_physicsEnabled = false;
|
_physicsEnabled = false;
|
||||||
|
_octreeProcessor.startEntitySequence();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -5498,7 +5499,7 @@ void Application::update(float deltaTime) {
|
||||||
// Check for flagged EntityData having arrived.
|
// Check for flagged EntityData having arrived.
|
||||||
auto entityTreeRenderer = getEntities();
|
auto entityTreeRenderer = getEntities();
|
||||||
if (isServerlessMode() ||
|
if (isServerlessMode() ||
|
||||||
(entityTreeRenderer && _octreeProcessor.octreeSequenceIsComplete(entityTreeRenderer->getLastOctreeMessageSequence()) )) {
|
(_octreeProcessor.isLoadSequenceComplete() )) {
|
||||||
// we've received a new full-scene octree stats packet, or it's been long enough to try again anyway
|
// we've received a new full-scene octree stats packet, or it's been long enough to try again anyway
|
||||||
_lastPhysicsCheckTime = now;
|
_lastPhysicsCheckTime = now;
|
||||||
_fullSceneCounterAtLastPhysicsCheck = _fullSceneReceivedCounter;
|
_fullSceneCounterAtLastPhysicsCheck = _fullSceneReceivedCounter;
|
||||||
|
@ -5507,7 +5508,7 @@ void Application::update(float deltaTime) {
|
||||||
// process octree stats packets are sent in between full sends of a scene (this isn't currently true).
|
// process octree stats packets are sent in between full sends of a scene (this isn't currently true).
|
||||||
// We keep physics disabled until we've received a full scene and everything near the avatar in that
|
// We keep physics disabled until we've received a full scene and everything near the avatar in that
|
||||||
// scene is ready to compute its collision shape.
|
// scene is ready to compute its collision shape.
|
||||||
if (nearbyEntitiesAreReadyForPhysics() && getMyAvatar()->isReadyForPhysics()) {
|
if (getMyAvatar()->isReadyForPhysics()) {
|
||||||
_physicsEnabled = true;
|
_physicsEnabled = true;
|
||||||
getMyAvatar()->updateMotionBehaviorFromMenu();
|
getMyAvatar()->updateMotionBehaviorFromMenu();
|
||||||
}
|
}
|
||||||
|
@ -6311,7 +6312,6 @@ void Application::clearDomainOctreeDetails() {
|
||||||
_octreeServerSceneStats.clear();
|
_octreeServerSceneStats.clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
_octreeProcessor.resetCompletionSequenceNumber();
|
|
||||||
// reset the model renderer
|
// reset the model renderer
|
||||||
getEntities()->clear();
|
getEntities()->clear();
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,11 @@
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
#include "SceneScriptingInterface.h"
|
#include "SceneScriptingInterface.h"
|
||||||
|
#include "SafeLanding.h"
|
||||||
|
|
||||||
OctreePacketProcessor::OctreePacketProcessor() {
|
OctreePacketProcessor::OctreePacketProcessor():
|
||||||
|
_safeLanding(new SafeLanding())
|
||||||
|
{
|
||||||
setObjectName("Octree Packet Processor");
|
setObjectName("Octree Packet Processor");
|
||||||
|
|
||||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||||
|
@ -26,6 +29,8 @@ OctreePacketProcessor::OctreePacketProcessor() {
|
||||||
packetReceiver.registerDirectListenerForTypes(octreePackets, this, "handleOctreePacket");
|
packetReceiver.registerDirectListenerForTypes(octreePackets, this, "handleOctreePacket");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OctreePacketProcessor::~OctreePacketProcessor() { }
|
||||||
|
|
||||||
void OctreePacketProcessor::handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
void OctreePacketProcessor::handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||||
queueReceivedPacket(message, senderNode);
|
queueReceivedPacket(message, senderNode);
|
||||||
}
|
}
|
||||||
|
@ -107,6 +112,7 @@ void OctreePacketProcessor::processPacket(QSharedPointer<ReceivedMessage> messag
|
||||||
auto renderer = qApp->getEntities();
|
auto renderer = qApp->getEntities();
|
||||||
if (renderer) {
|
if (renderer) {
|
||||||
renderer->processDatagram(*message, sendingNode);
|
renderer->processDatagram(*message, sendingNode);
|
||||||
|
_safeLanding->noteReceivedsequenceNumber(renderer->getLastOctreeMessageSequence());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
@ -115,8 +121,7 @@ void OctreePacketProcessor::processPacket(QSharedPointer<ReceivedMessage> messag
|
||||||
// Read sequence #
|
// Read sequence #
|
||||||
OCTREE_PACKET_SEQUENCE completionNumber;
|
OCTREE_PACKET_SEQUENCE completionNumber;
|
||||||
message->readPrimitive(&completionNumber);
|
message->readPrimitive(&completionNumber);
|
||||||
|
_safeLanding->setCompletionSequenceNumbers(0, completionNumber);
|
||||||
_completionSequenceNumber = completionNumber;
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
|
@ -125,22 +130,10 @@ void OctreePacketProcessor::processPacket(QSharedPointer<ReceivedMessage> messag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OctreePacketProcessor::resetCompletionSequenceNumber() {
|
void OctreePacketProcessor::startEntitySequence() {
|
||||||
_completionSequenceNumber = INVALID_SEQUENCE;
|
_safeLanding->startEntitySequence(qApp->getEntities());
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
bool OctreePacketProcessor::isLoadSequenceComplete() const {
|
||||||
template<typename T> bool lessThanWraparound(int a, int b) {
|
return _safeLanding->isLoadSequenceComplete();
|
||||||
constexpr int MAX_T_VALUE = std::numeric_limits<T>::max();
|
|
||||||
if (b <= a) {
|
|
||||||
b += MAX_T_VALUE;
|
|
||||||
}
|
|
||||||
return (b - a) < (MAX_T_VALUE / 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OctreePacketProcessor::octreeSequenceIsComplete(int sequenceNumber) const {
|
|
||||||
// If we've received the flagged seq # and the current one is >= it.
|
|
||||||
return _completionSequenceNumber != INVALID_SEQUENCE &&
|
|
||||||
!lessThanWraparound<OCTREE_PACKET_SEQUENCE>(sequenceNumber, _completionSequenceNumber);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,21 +15,22 @@
|
||||||
#include <ReceivedPacketProcessor.h>
|
#include <ReceivedPacketProcessor.h>
|
||||||
#include <ReceivedMessage.h>
|
#include <ReceivedMessage.h>
|
||||||
|
|
||||||
|
class SafeLanding;
|
||||||
|
|
||||||
/// Handles processing of incoming voxel packets for the interface application. As with other ReceivedPacketProcessor classes
|
/// Handles processing of incoming voxel packets for the interface application. As with other ReceivedPacketProcessor classes
|
||||||
/// the user is responsible for reading inbound packets and adding them to the processing queue by calling queueReceivedPacket()
|
/// the user is responsible for reading inbound packets and adding them to the processing queue by calling queueReceivedPacket()
|
||||||
class OctreePacketProcessor : public ReceivedPacketProcessor {
|
class OctreePacketProcessor : public ReceivedPacketProcessor {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
OctreePacketProcessor();
|
OctreePacketProcessor();
|
||||||
|
~OctreePacketProcessor();
|
||||||
|
|
||||||
bool octreeSequenceIsComplete(int sequenceNumber) const;
|
void startEntitySequence();
|
||||||
|
bool isLoadSequenceComplete() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void packetVersionMismatch();
|
void packetVersionMismatch();
|
||||||
|
|
||||||
public slots:
|
|
||||||
void resetCompletionSequenceNumber();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void processPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) override;
|
virtual void processPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) override;
|
||||||
|
|
||||||
|
@ -37,8 +38,6 @@ private slots:
|
||||||
void handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
void handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr int INVALID_SEQUENCE = -1;
|
std::unique_ptr<SafeLanding> _safeLanding;
|
||||||
std::atomic<int> _completionSequenceNumber { INVALID_SEQUENCE };
|
|
||||||
|
|
||||||
};
|
};
|
||||||
#endif // hifi_OctreePacketProcessor_h
|
#endif // hifi_OctreePacketProcessor_h
|
||||||
|
|
172
interface/src/octree/SafeLanding.cpp
Normal file
172
interface/src/octree/SafeLanding.cpp
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
//
|
||||||
|
// SafeLanding.cpp
|
||||||
|
// interface/src/octree
|
||||||
|
//
|
||||||
|
// Created by Simon Walton.
|
||||||
|
// 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 "SafeLanding.h"
|
||||||
|
#include "EntityTreeRenderer.h"
|
||||||
|
#include "ModelEntityItem.h"
|
||||||
|
#include "InterfaceLogging.h"
|
||||||
|
|
||||||
|
const int SafeLanding::SEQUENCE_MODULO = std::numeric_limits<OCTREE_PACKET_SEQUENCE>::max() + 1;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
template<typename T> bool lessThanWraparound(int a, int b) {
|
||||||
|
constexpr int MAX_T_VALUE = std::numeric_limits<T>::max();
|
||||||
|
if (b <= a) {
|
||||||
|
b += MAX_T_VALUE;
|
||||||
|
}
|
||||||
|
return (b - a) < (MAX_T_VALUE / 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SafeLanding::SequenceLessThan::operator()(const int& a, const int& b) const {
|
||||||
|
return lessThanWraparound<OCTREE_PACKET_SEQUENCE>(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SafeLanding::startEntitySequence(QSharedPointer<EntityTreeRenderer> entityTreeRenderer) {
|
||||||
|
auto entityTree = entityTreeRenderer->getTree();
|
||||||
|
|
||||||
|
if (entityTree) {
|
||||||
|
Locker lock(_lock);
|
||||||
|
_entityTree = entityTree;
|
||||||
|
_trackedEntities.clear();
|
||||||
|
_trackingEntities = true;
|
||||||
|
connect(std::const_pointer_cast<EntityTree>(_entityTree).get(),
|
||||||
|
&EntityTree::addingEntity, this, &SafeLanding::addTrackedEntity);
|
||||||
|
connect(std::const_pointer_cast<EntityTree>(_entityTree).get(),
|
||||||
|
&EntityTree::deletingEntity, this, &SafeLanding::deleteTrackedEntity);
|
||||||
|
|
||||||
|
_sequenceNumbers.clear();
|
||||||
|
_initialStart = INVALID_SEQUENCE;
|
||||||
|
_initialEnd = INVALID_SEQUENCE;
|
||||||
|
EntityTreeRenderer::setEntityLoadingPriorityFunction(&ElevatedPriority);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SafeLanding::stopEntitySequence() {
|
||||||
|
Locker lock(_lock);
|
||||||
|
_trackingEntities = false;
|
||||||
|
_initialStart = INVALID_SEQUENCE;
|
||||||
|
_initialEnd = INVALID_SEQUENCE;
|
||||||
|
_trackedEntities.clear();
|
||||||
|
_sequenceNumbers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SafeLanding::addTrackedEntity(const EntityItemID& entityID) {
|
||||||
|
if (_trackingEntities) {
|
||||||
|
Locker lock(_lock);
|
||||||
|
EntityItemPointer entity = _entityTree->findEntityByID(entityID);
|
||||||
|
|
||||||
|
if (entity && !entity->getCollisionless()) {
|
||||||
|
const auto& entityType = entity->getType();
|
||||||
|
if (entityType == EntityTypes::Model) {
|
||||||
|
ModelEntityItem * modelEntity = std::dynamic_pointer_cast<ModelEntityItem>(entity).get();
|
||||||
|
static const std::set<ShapeType> downloadedCollisionTypes
|
||||||
|
{ SHAPE_TYPE_COMPOUND, SHAPE_TYPE_SIMPLE_COMPOUND, SHAPE_TYPE_STATIC_MESH, SHAPE_TYPE_SIMPLE_HULL };
|
||||||
|
bool hasAABox;
|
||||||
|
entity->getAABox(hasAABox);
|
||||||
|
if (hasAABox && downloadedCollisionTypes.count(modelEntity->getShapeType()) != 0) {
|
||||||
|
// Only track entities with downloaded collision bodies.
|
||||||
|
_trackedEntities.emplace(entityID, entity);
|
||||||
|
qCDebug(interfaceapp) << "Safe Landing: Tracking entity " << entity->getItemName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SafeLanding::deleteTrackedEntity(const EntityItemID& entityID) {
|
||||||
|
Locker lock(_lock);
|
||||||
|
_trackedEntities.erase(entityID);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SafeLanding::setCompletionSequenceNumbers(int first, int last) {
|
||||||
|
Locker lock(_lock);
|
||||||
|
if (_initialStart == INVALID_SEQUENCE) {
|
||||||
|
_initialStart = first;
|
||||||
|
_initialEnd = last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SafeLanding::noteReceivedsequenceNumber(int sequenceNumber) {
|
||||||
|
if (_trackingEntities) {
|
||||||
|
Locker lock(_lock);
|
||||||
|
_sequenceNumbers.insert(sequenceNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SafeLanding::isLoadSequenceComplete() {
|
||||||
|
if (isEntityPhysicsComplete() && isSequenceNumbersComplete()) {
|
||||||
|
Locker lock(_lock);
|
||||||
|
_trackedEntities.clear();
|
||||||
|
_initialStart = INVALID_SEQUENCE;
|
||||||
|
_initialEnd = INVALID_SEQUENCE;
|
||||||
|
_entityTree = nullptr;
|
||||||
|
EntityTreeRenderer::setEntityLoadingPriorityFunction(StandardPriority);
|
||||||
|
qCDebug(interfaceapp) << "Safe Landing: load sequence complete";
|
||||||
|
}
|
||||||
|
|
||||||
|
return !_trackingEntities;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SafeLanding::isSequenceNumbersComplete() {
|
||||||
|
if (_initialStart != INVALID_SEQUENCE) {
|
||||||
|
Locker lock(_lock);
|
||||||
|
int sequenceSize = _initialStart <= _initialEnd ? _initialEnd - _initialStart:
|
||||||
|
_initialEnd + SEQUENCE_MODULO - _initialStart;
|
||||||
|
auto startIter = _sequenceNumbers.find(_initialStart);
|
||||||
|
auto endIter = _sequenceNumbers.find(_initialEnd);
|
||||||
|
if (sequenceSize == 0 ||
|
||||||
|
(startIter != _sequenceNumbers.end()
|
||||||
|
&& endIter != _sequenceNumbers.end()
|
||||||
|
&& distance(startIter, endIter) == sequenceSize) ) {
|
||||||
|
_trackingEntities = false; // Don't track anything else that comes in.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SafeLanding::isEntityPhysicsComplete() {
|
||||||
|
Locker lock(_lock);
|
||||||
|
for (auto entityMapIter = _trackedEntities.begin(); entityMapIter != _trackedEntities.end(); ++entityMapIter) {
|
||||||
|
auto entity = entityMapIter->second;
|
||||||
|
if (!entity->shouldBePhysical() || entity->isReadyToComputeShape()) {
|
||||||
|
entityMapIter = _trackedEntities.erase(entityMapIter);
|
||||||
|
if (entityMapIter == _trackedEntities.end()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _trackedEntities.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
float SafeLanding::ElevatedPriority(const EntityItem& entityItem) {
|
||||||
|
return entityItem.getCollisionless() ? 0.0f : 10.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SafeLanding::debugDumpSequenceIDs() const {
|
||||||
|
int p = -1;
|
||||||
|
qCDebug(interfaceapp) << "Sequence set size:" << _sequenceNumbers.size();
|
||||||
|
for (auto s: _sequenceNumbers) {
|
||||||
|
if (p == -1) {
|
||||||
|
p = s;
|
||||||
|
qCDebug(interfaceapp) << "First:" << s;
|
||||||
|
} else {
|
||||||
|
if (s != p + 1) {
|
||||||
|
qCDebug(interfaceapp) << "Gap from" << p << "to" << s << "(exclusive)";
|
||||||
|
p = s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (p != -1) {
|
||||||
|
qCDebug(interfaceapp) << "Last:" << p;
|
||||||
|
}
|
||||||
|
}
|
65
interface/src/octree/SafeLanding.h
Normal file
65
interface/src/octree/SafeLanding.h
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
//
|
||||||
|
// SafeLanding.h
|
||||||
|
// interface/src/octree
|
||||||
|
//
|
||||||
|
// Created by Simon Walton.
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
|
||||||
|
// Controller for logic to wait for local collision bodies before enabling physics.
|
||||||
|
|
||||||
|
#ifndef hifi_SafeLanding_h
|
||||||
|
#define hifi_SafeLanding_h
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtCore/QSharedPointer>
|
||||||
|
|
||||||
|
#include "EntityItem.h"
|
||||||
|
|
||||||
|
class EntityTreeRenderer;
|
||||||
|
class EntityItemID;
|
||||||
|
|
||||||
|
class SafeLanding : public QObject {
|
||||||
|
public:
|
||||||
|
void startEntitySequence(QSharedPointer<EntityTreeRenderer> entityTreeRenderer);
|
||||||
|
void stopEntitySequence();
|
||||||
|
void setCompletionSequenceNumbers(int first, int last);
|
||||||
|
void noteReceivedsequenceNumber(int sequenceNumber);
|
||||||
|
bool isLoadSequenceComplete();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void addTrackedEntity(const EntityItemID& entityID);
|
||||||
|
void deleteTrackedEntity(const EntityItemID& entityID);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool isSequenceNumbersComplete();
|
||||||
|
void debugDumpSequenceIDs() const;
|
||||||
|
bool isEntityPhysicsComplete();
|
||||||
|
|
||||||
|
std::mutex _lock;
|
||||||
|
using Locker = std::lock_guard<std::mutex>;
|
||||||
|
bool _trackingEntities { false };
|
||||||
|
EntityTreePointer _entityTree;
|
||||||
|
using EntityMap = std::map<EntityItemID, EntityItemPointer>;
|
||||||
|
EntityMap _trackedEntities;
|
||||||
|
|
||||||
|
static constexpr int INVALID_SEQUENCE = -1;
|
||||||
|
int _initialStart { INVALID_SEQUENCE };
|
||||||
|
int _initialEnd { INVALID_SEQUENCE };
|
||||||
|
|
||||||
|
struct SequenceLessThan {
|
||||||
|
bool operator()(const int& a, const int& b) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::set<int, SequenceLessThan> _sequenceNumbers;
|
||||||
|
|
||||||
|
static float ElevatedPriority(const EntityItem& entityItem);
|
||||||
|
static float StandardPriority(const EntityItem&) { return 0.0f; }
|
||||||
|
|
||||||
|
static const int SEQUENCE_MODULO;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_SafeLanding_h
|
|
@ -12,9 +12,33 @@
|
||||||
|
|
||||||
#include <glm/gtx/transform.hpp>
|
#include <glm/gtx/transform.hpp>
|
||||||
|
|
||||||
|
#include "PhysicsCollisionGroups.h"
|
||||||
#include "ScriptEngineLogging.h"
|
#include "ScriptEngineLogging.h"
|
||||||
#include "UUIDHasher.h"
|
#include "UUIDHasher.h"
|
||||||
|
|
||||||
|
PickResultPointer CollisionPickResult::compareAndProcessNewResult(const PickResultPointer& newRes) {
|
||||||
|
const std::shared_ptr<CollisionPickResult> newCollisionResult = std::static_pointer_cast<CollisionPickResult>(newRes);
|
||||||
|
|
||||||
|
if (entityIntersections.size()) {
|
||||||
|
entityIntersections.insert(entityIntersections.cend(), newCollisionResult->entityIntersections.begin(), newCollisionResult->entityIntersections.end());
|
||||||
|
} else {
|
||||||
|
entityIntersections = newCollisionResult->entityIntersections;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (avatarIntersections.size()) {
|
||||||
|
avatarIntersections.insert(avatarIntersections.cend(), newCollisionResult->avatarIntersections.begin(), newCollisionResult->avatarIntersections.end());
|
||||||
|
} else {
|
||||||
|
avatarIntersections = newCollisionResult->avatarIntersections;
|
||||||
|
}
|
||||||
|
|
||||||
|
intersects = entityIntersections.size() || avatarIntersections.size();
|
||||||
|
if (newCollisionResult->loadState == LOAD_STATE_NOT_LOADED || loadState == LOAD_STATE_UNKNOWN) {
|
||||||
|
loadState = (LoadState)newCollisionResult->loadState;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_shared<CollisionPickResult>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
void buildObjectIntersectionsMap(IntersectionType intersectionType, const std::vector<ContactTestResult>& objectIntersections, std::unordered_map<QUuid, QVariantMap>& intersections, std::unordered_map<QUuid, QVariantList>& collisionPointPairs) {
|
void buildObjectIntersectionsMap(IntersectionType intersectionType, const std::vector<ContactTestResult>& objectIntersections, std::unordered_map<QUuid, QVariantMap>& intersections, std::unordered_map<QUuid, QVariantList>& collisionPointPairs) {
|
||||||
for (auto& objectIntersection : objectIntersections) {
|
for (auto& objectIntersection : objectIntersections) {
|
||||||
auto at = intersections.find(objectIntersection.foundID);
|
auto at = intersections.find(objectIntersection.foundID);
|
||||||
|
@ -308,20 +332,27 @@ CollisionRegion CollisionPick::getMathematicalPick() const {
|
||||||
return _mathPick;
|
return _mathPick;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<ContactTestResult> CollisionPick::filterIntersections(const std::vector<ContactTestResult>& intersections) const {
|
void CollisionPick::filterIntersections(std::vector<ContactTestResult>& intersections) const {
|
||||||
std::vector<ContactTestResult> filteredIntersections;
|
|
||||||
|
|
||||||
const QVector<QUuid>& ignoreItems = getIgnoreItems();
|
const QVector<QUuid>& ignoreItems = getIgnoreItems();
|
||||||
const QVector<QUuid>& includeItems = getIncludeItems();
|
const QVector<QUuid>& includeItems = getIncludeItems();
|
||||||
bool isWhitelist = includeItems.size();
|
bool isWhitelist = !includeItems.empty();
|
||||||
for (const auto& intersection : intersections) {
|
|
||||||
|
if (!isWhitelist && ignoreItems.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ContactTestResult> filteredIntersections;
|
||||||
|
|
||||||
|
int n = (int)intersections.size();
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
auto& intersection = intersections[i];
|
||||||
const QUuid& id = intersection.foundID;
|
const QUuid& id = intersection.foundID;
|
||||||
if (!ignoreItems.contains(id) && (!isWhitelist || includeItems.contains(id))) {
|
if (!ignoreItems.contains(id) && (!isWhitelist || includeItems.contains(id))) {
|
||||||
filteredIntersections.push_back(intersection);
|
filteredIntersections.push_back(intersection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return filteredIntersections;
|
intersections = filteredIntersections;
|
||||||
}
|
}
|
||||||
|
|
||||||
PickResultPointer CollisionPick::getEntityIntersection(const CollisionRegion& pick) {
|
PickResultPointer CollisionPick::getEntityIntersection(const CollisionRegion& pick) {
|
||||||
|
@ -330,7 +361,8 @@ PickResultPointer CollisionPick::getEntityIntersection(const CollisionRegion& pi
|
||||||
return std::make_shared<CollisionPickResult>(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
|
return std::make_shared<CollisionPickResult>(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& entityIntersections = filterIntersections(_physicsEngine->getCollidingInRegion(MOTIONSTATE_TYPE_ENTITY, *pick.shapeInfo, pick.transform));
|
auto entityIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_ENTITIES, *pick.shapeInfo, pick.transform);
|
||||||
|
filterIntersections(entityIntersections);
|
||||||
return std::make_shared<CollisionPickResult>(pick, CollisionPickResult::LOAD_STATE_LOADED, entityIntersections, std::vector<ContactTestResult>());
|
return std::make_shared<CollisionPickResult>(pick, CollisionPickResult::LOAD_STATE_LOADED, entityIntersections, std::vector<ContactTestResult>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,8 +375,9 @@ PickResultPointer CollisionPick::getAvatarIntersection(const CollisionRegion& pi
|
||||||
// Cannot compute result
|
// Cannot compute result
|
||||||
return std::make_shared<CollisionPickResult>(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
|
return std::make_shared<CollisionPickResult>(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& avatarIntersections = filterIntersections(_physicsEngine->getCollidingInRegion(MOTIONSTATE_TYPE_AVATAR, *pick.shapeInfo, pick.transform));
|
auto avatarIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_AVATARS, *pick.shapeInfo, pick.transform);
|
||||||
|
filterIntersections(avatarIntersections);
|
||||||
return std::make_shared<CollisionPickResult>(pick, CollisionPickResult::LOAD_STATE_LOADED, std::vector<ContactTestResult>(), avatarIntersections);
|
return std::make_shared<CollisionPickResult>(pick, CollisionPickResult::LOAD_STATE_LOADED, std::vector<ContactTestResult>(), avatarIntersections);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,23 +49,7 @@ public:
|
||||||
bool doesIntersect() const override { return intersects; }
|
bool doesIntersect() const override { return intersects; }
|
||||||
bool checkOrFilterAgainstMaxDistance(float maxDistance) override { return true; }
|
bool checkOrFilterAgainstMaxDistance(float maxDistance) override { return true; }
|
||||||
|
|
||||||
PickResultPointer compareAndProcessNewResult(const PickResultPointer& newRes) override {
|
PickResultPointer compareAndProcessNewResult(const PickResultPointer& newRes) override;
|
||||||
const std::shared_ptr<CollisionPickResult> newCollisionResult = std::static_pointer_cast<CollisionPickResult>(newRes);
|
|
||||||
|
|
||||||
for (ContactTestResult& entityIntersection : newCollisionResult->entityIntersections) {
|
|
||||||
entityIntersections.push_back(entityIntersection);
|
|
||||||
}
|
|
||||||
for (ContactTestResult& avatarIntersection : newCollisionResult->avatarIntersections) {
|
|
||||||
avatarIntersections.push_back(avatarIntersection);
|
|
||||||
}
|
|
||||||
|
|
||||||
intersects = entityIntersections.size() || avatarIntersections.size();
|
|
||||||
if (newCollisionResult->loadState == LOAD_STATE_NOT_LOADED || loadState == LOAD_STATE_UNKNOWN) {
|
|
||||||
loadState = (LoadState)newCollisionResult->loadState;
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::make_shared<CollisionPickResult>(*this);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class CollisionPick : public Pick<CollisionRegion> {
|
class CollisionPick : public Pick<CollisionRegion> {
|
||||||
|
@ -92,7 +76,7 @@ protected:
|
||||||
// Returns true if pick.shapeInfo is valid. Otherwise, attempts to get the shapeInfo ready for use.
|
// Returns true if pick.shapeInfo is valid. Otherwise, attempts to get the shapeInfo ready for use.
|
||||||
bool isShapeInfoReady();
|
bool isShapeInfoReady();
|
||||||
void computeShapeInfo(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource);
|
void computeShapeInfo(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource);
|
||||||
const std::vector<ContactTestResult> filterIntersections(const std::vector<ContactTestResult>& intersections) const;
|
void filterIntersections(std::vector<ContactTestResult>& intersections) const;
|
||||||
|
|
||||||
CollisionRegion _mathPick;
|
CollisionRegion _mathPick;
|
||||||
PhysicsEnginePointer _physicsEngine;
|
PhysicsEnginePointer _physicsEngine;
|
||||||
|
|
|
@ -642,6 +642,14 @@ bool EntityTreeRenderer::applyLayeredZones() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode) {
|
void EntityTreeRenderer::processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode) {
|
||||||
|
OCTREE_PACKET_FLAGS flags;
|
||||||
|
message.readPrimitive(&flags);
|
||||||
|
|
||||||
|
OCTREE_PACKET_SEQUENCE sequence;
|
||||||
|
message.readPrimitive(&sequence);
|
||||||
|
|
||||||
|
_lastOctreeMessageSequence = sequence;
|
||||||
|
message.seek(0);
|
||||||
std::static_pointer_cast<EntityTree>(_tree)->processEraseMessage(message, sourceNode);
|
std::static_pointer_cast<EntityTree>(_tree)->processEraseMessage(message, sourceNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -212,6 +212,8 @@ GLShader* GLBackend::compileBackendProgram(const Shader& program, const Shader::
|
||||||
glprogram = ::gl::buildProgram(shaderGLObjects);
|
glprogram = ::gl::buildProgram(shaderGLObjects);
|
||||||
|
|
||||||
if (!::gl::linkProgram(glprogram, compilationLogs[version].message)) {
|
if (!::gl::linkProgram(glprogram, compilationLogs[version].message)) {
|
||||||
|
qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Program didn't link:\n" << compilationLogs[version].message.c_str();
|
||||||
|
compilationLogs[version].compiled = false;
|
||||||
glDeleteProgram(glprogram);
|
glDeleteProgram(glprogram);
|
||||||
glprogram = 0;
|
glprogram = 0;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -254,7 +256,7 @@ GLint GLBackend::getRealUniformLocation(GLint location) const {
|
||||||
// uniforms. If someone is requesting a uniform that isn't in the remapping structure
|
// uniforms. If someone is requesting a uniform that isn't in the remapping structure
|
||||||
// that's a bug from the calling code, because it means that location wasn't in the
|
// that's a bug from the calling code, because it means that location wasn't in the
|
||||||
// reflection
|
// reflection
|
||||||
qWarning() << "Unexpected location requested for shader";
|
qWarning() << "Unexpected location requested for shader: #" << location;
|
||||||
return INVALID_UNIFORM_INDEX;
|
return INVALID_UNIFORM_INDEX;
|
||||||
}
|
}
|
||||||
return itr->second;
|
return itr->second;
|
||||||
|
|
|
@ -28,24 +28,6 @@
|
||||||
#include "PhysicsLogging.h"
|
#include "PhysicsLogging.h"
|
||||||
|
|
||||||
|
|
||||||
static bool flipNormalsMyAvatarVsBackfacingTriangles( btManifoldPoint& cp,
|
|
||||||
const btCollisionObjectWrapper* colObj0Wrap, int partId0, int index0,
|
|
||||||
const btCollisionObjectWrapper* colObj1Wrap, int partId1, int index1) {
|
|
||||||
if (colObj1Wrap->getCollisionShape()->getShapeType() == TRIANGLE_SHAPE_PROXYTYPE) {
|
|
||||||
auto triShape = static_cast<const btTriangleShape*>(colObj1Wrap->getCollisionShape());
|
|
||||||
const btVector3* v = triShape->m_vertices1;
|
|
||||||
btVector3 faceNormal = colObj1Wrap->getWorldTransform().getBasis() * btCross(v[1] - v[0], v[2] - v[0]);
|
|
||||||
float nDotF = btDot(faceNormal, cp.m_normalWorldOnB);
|
|
||||||
if (nDotF <= 0.0f) {
|
|
||||||
faceNormal.normalize();
|
|
||||||
// flip the contact normal to be aligned with the face normal
|
|
||||||
cp.m_normalWorldOnB += -2.0f * nDotF * faceNormal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// return value is currently ignored but to be future-proof: return false when not modifying friction
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
PhysicsEngine::PhysicsEngine(const glm::vec3& offset) :
|
PhysicsEngine::PhysicsEngine(const glm::vec3& offset) :
|
||||||
_originOffset(offset),
|
_originOffset(offset),
|
||||||
_myAvatarController(nullptr) {
|
_myAvatarController(nullptr) {
|
||||||
|
@ -88,9 +70,6 @@ void PhysicsEngine::init() {
|
||||||
// in order for its broadphase collision queries to work correctly. Look at how we use
|
// in order for its broadphase collision queries to work correctly. Look at how we use
|
||||||
// _activeStaticBodies to track and update the Aabb's of moved static objects.
|
// _activeStaticBodies to track and update the Aabb's of moved static objects.
|
||||||
_dynamicsWorld->setForceUpdateAllAabbs(false);
|
_dynamicsWorld->setForceUpdateAllAabbs(false);
|
||||||
|
|
||||||
// register contact filter to help MyAvatar pass through backfacing triangles
|
|
||||||
gContactAddedCallback = flipNormalsMyAvatarVsBackfacingTriangles;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -864,9 +843,8 @@ void PhysicsEngine::setShowBulletConstraintLimits(bool value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AllContactsCallback : public btCollisionWorld::ContactResultCallback {
|
struct AllContactsCallback : public btCollisionWorld::ContactResultCallback {
|
||||||
AllContactsCallback(MotionStateType desiredObjectType, const ShapeInfo& shapeInfo, const Transform& transform, btCollisionObject* myAvatarCollisionObject) :
|
AllContactsCallback(int32_t mask, int32_t group, const ShapeInfo& shapeInfo, const Transform& transform, btCollisionObject* myAvatarCollisionObject) :
|
||||||
btCollisionWorld::ContactResultCallback(),
|
btCollisionWorld::ContactResultCallback(),
|
||||||
desiredObjectType(desiredObjectType),
|
|
||||||
collisionObject(),
|
collisionObject(),
|
||||||
contacts(),
|
contacts(),
|
||||||
myAvatarCollisionObject(myAvatarCollisionObject) {
|
myAvatarCollisionObject(myAvatarCollisionObject) {
|
||||||
|
@ -879,21 +857,19 @@ struct AllContactsCallback : public btCollisionWorld::ContactResultCallback {
|
||||||
bulletTransform.setRotation(glmToBullet(transform.getRotation()));
|
bulletTransform.setRotation(glmToBullet(transform.getRotation()));
|
||||||
|
|
||||||
collisionObject.setWorldTransform(bulletTransform);
|
collisionObject.setWorldTransform(bulletTransform);
|
||||||
|
|
||||||
|
m_collisionFilterMask = mask;
|
||||||
|
m_collisionFilterGroup = group;
|
||||||
}
|
}
|
||||||
|
|
||||||
~AllContactsCallback() {
|
~AllContactsCallback() {
|
||||||
ObjectMotionState::getShapeManager()->releaseShape(collisionObject.getCollisionShape());
|
ObjectMotionState::getShapeManager()->releaseShape(collisionObject.getCollisionShape());
|
||||||
}
|
}
|
||||||
|
|
||||||
MotionStateType desiredObjectType;
|
|
||||||
btCollisionObject collisionObject;
|
btCollisionObject collisionObject;
|
||||||
std::vector<ContactTestResult> contacts;
|
std::vector<ContactTestResult> contacts;
|
||||||
btCollisionObject* myAvatarCollisionObject;
|
btCollisionObject* myAvatarCollisionObject;
|
||||||
|
|
||||||
bool needsCollision(btBroadphaseProxy* proxy) const override {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObjectWrapper* colObj0, int partId0, int index0, const btCollisionObjectWrapper* colObj1, int partId1, int index1) override {
|
btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObjectWrapper* colObj0, int partId0, int index0, const btCollisionObjectWrapper* colObj1, int partId1, int index1) override {
|
||||||
const btCollisionObject* otherBody;
|
const btCollisionObject* otherBody;
|
||||||
btVector3 penetrationPoint;
|
btVector3 penetrationPoint;
|
||||||
|
@ -909,7 +885,7 @@ struct AllContactsCallback : public btCollisionWorld::ContactResultCallback {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Give MyAvatar a motion state so we don't have to do this
|
// TODO: Give MyAvatar a motion state so we don't have to do this
|
||||||
if (desiredObjectType == MOTIONSTATE_TYPE_AVATAR && myAvatarCollisionObject && myAvatarCollisionObject == otherBody) {
|
if ((m_collisionFilterMask & BULLET_COLLISION_GROUP_MY_AVATAR) && myAvatarCollisionObject && myAvatarCollisionObject == otherBody) {
|
||||||
contacts.emplace_back(Physics::getSessionUUID(), bulletToGLM(penetrationPoint), bulletToGLM(otherPenetrationPoint));
|
contacts.emplace_back(Physics::getSessionUUID(), bulletToGLM(penetrationPoint), bulletToGLM(otherPenetrationPoint));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -921,7 +897,7 @@ struct AllContactsCallback : public btCollisionWorld::ContactResultCallback {
|
||||||
|
|
||||||
const btMotionState* motionStateCandidate = collisionCandidate->getMotionState();
|
const btMotionState* motionStateCandidate = collisionCandidate->getMotionState();
|
||||||
const ObjectMotionState* candidate = dynamic_cast<const ObjectMotionState*>(motionStateCandidate);
|
const ObjectMotionState* candidate = dynamic_cast<const ObjectMotionState*>(motionStateCandidate);
|
||||||
if (!candidate || candidate->getType() != desiredObjectType) {
|
if (!candidate) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -937,14 +913,14 @@ protected:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::vector<ContactTestResult> PhysicsEngine::getCollidingInRegion(MotionStateType desiredObjectType, const ShapeInfo& regionShapeInfo, const Transform& regionTransform) const {
|
std::vector<ContactTestResult> PhysicsEngine::contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group) const {
|
||||||
// TODO: Give MyAvatar a motion state so we don't have to do this
|
// TODO: Give MyAvatar a motion state so we don't have to do this
|
||||||
btCollisionObject* myAvatarCollisionObject = nullptr;
|
btCollisionObject* myAvatarCollisionObject = nullptr;
|
||||||
if (desiredObjectType == MOTIONSTATE_TYPE_AVATAR && _myAvatarController) {
|
if ((mask & USER_COLLISION_GROUP_MY_AVATAR) && _myAvatarController) {
|
||||||
myAvatarCollisionObject = _myAvatarController->getCollisionObject();
|
myAvatarCollisionObject = _myAvatarController->getCollisionObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto contactCallback = AllContactsCallback(desiredObjectType, regionShapeInfo, regionTransform, myAvatarCollisionObject);
|
auto contactCallback = AllContactsCallback((int32_t)mask, (int32_t)group, regionShapeInfo, regionTransform, myAvatarCollisionObject);
|
||||||
_dynamicsWorld->contactTest(&contactCallback.collisionObject, contactCallback);
|
_dynamicsWorld->contactTest(&contactCallback.collisionObject, contactCallback);
|
||||||
|
|
||||||
return contactCallback.contacts;
|
return contactCallback.contacts;
|
||||||
|
|
|
@ -125,8 +125,9 @@ public:
|
||||||
void setShowBulletConstraints(bool value);
|
void setShowBulletConstraints(bool value);
|
||||||
void setShowBulletConstraintLimits(bool value);
|
void setShowBulletConstraintLimits(bool value);
|
||||||
|
|
||||||
// Function for getting colliding ObjectMotionStates in the world of specified type
|
// Function for getting colliding objects in the world of specified type
|
||||||
const std::vector<ContactTestResult> getCollidingInRegion(MotionStateType desiredObjectType, const ShapeInfo& regionShapeInfo, const Transform& regionTransform) const;
|
// See PhysicsCollisionGroups.h for mask flags.
|
||||||
|
std::vector<ContactTestResult> contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group = USER_COLLISION_GROUP_DYNAMIC) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QList<EntityDynamicPointer> removeDynamicsForBody(btRigidBody* body);
|
QList<EntityDynamicPointer> removeDynamicsForBody(btRigidBody* body);
|
||||||
|
|
|
@ -496,10 +496,6 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext,
|
||||||
batch.setPipeline(program);
|
batch.setPipeline(program);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjust the texcoordTransform in the case we are rendeirng a sub region(mini mirror)
|
|
||||||
auto textureFrameTransform = gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(deferredFramebuffer->getFrameSize(), args->_viewport);
|
|
||||||
batch._glUniform4fv(ru::Uniform::TexcoordTransform, 1, reinterpret_cast< const float* >(&textureFrameTransform));
|
|
||||||
|
|
||||||
// Setup the global lighting
|
// Setup the global lighting
|
||||||
deferredLightingEffect->setupKeyLightBatch(args, batch);
|
deferredLightingEffect->setupKeyLightBatch(args, batch);
|
||||||
|
|
||||||
|
@ -560,23 +556,18 @@ void RenderDeferredLocals::run(const render::RenderContextPointer& renderContext
|
||||||
batch.setViewportTransform(viewport);
|
batch.setViewportTransform(viewport);
|
||||||
batch.setStateScissorRect(viewport);
|
batch.setStateScissorRect(viewport);
|
||||||
|
|
||||||
auto textureFrameTransform = gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(deferredFramebuffer->getFrameSize(), viewport);
|
|
||||||
|
|
||||||
|
|
||||||
auto& lightIndices = lightClusters->_visibleLightIndices;
|
auto& lightIndices = lightClusters->_visibleLightIndices;
|
||||||
if (!lightIndices.empty() && lightIndices[0] > 0) {
|
if (!lightIndices.empty() && lightIndices[0] > 0) {
|
||||||
deferredLightingEffect->setupLocalLightsBatch(batch, lightClusters);
|
deferredLightingEffect->setupLocalLightsBatch(batch, lightClusters);
|
||||||
|
|
||||||
// Local light pipeline
|
// Local light pipeline
|
||||||
batch.setPipeline(deferredLightingEffect->_localLight);
|
batch.setPipeline(deferredLightingEffect->_localLight);
|
||||||
batch._glUniform4fv(ru::Uniform::TexcoordTransform, 1, reinterpret_cast<const float*>(&textureFrameTransform));
|
|
||||||
|
|
||||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||||
|
|
||||||
// Draw outline as well ?
|
// Draw outline as well ?
|
||||||
if (lightingModel->isShowLightContourEnabled()) {
|
if (lightingModel->isShowLightContourEnabled()) {
|
||||||
batch.setPipeline(deferredLightingEffect->_localLightOutline);
|
batch.setPipeline(deferredLightingEffect->_localLightOutline);
|
||||||
batch._glUniform4fv(ru::Uniform::TexcoordTransform, 1, reinterpret_cast<const float*>(&textureFrameTransform));
|
|
||||||
|
|
||||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||||
}
|
}
|
||||||
|
|
|
@ -282,6 +282,7 @@ void Model::reset() {
|
||||||
const FBXGeometry& geometry = getFBXGeometry();
|
const FBXGeometry& geometry = getFBXGeometry();
|
||||||
_rig.reset(geometry);
|
_rig.reset(geometry);
|
||||||
emit rigReset();
|
emit rigReset();
|
||||||
|
emit rigReady();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,6 @@
|
||||||
|
|
||||||
layout(location=RENDER_UTILS_ATTR_TEXCOORD01) out vec4 _texCoord01;
|
layout(location=RENDER_UTILS_ATTR_TEXCOORD01) out vec4 _texCoord01;
|
||||||
|
|
||||||
layout(location=RENDER_UTILS_UNIFORM_LIGHT_TEXCOORD_TRANSFORM) uniform vec4 texcoordFrameTransform;
|
|
||||||
|
|
||||||
void main(void) {
|
void main(void) {
|
||||||
const float depth = 1.0;
|
const float depth = 1.0;
|
||||||
const vec4 UNIT_QUAD[4] = vec4[4](
|
const vec4 UNIT_QUAD[4] = vec4[4](
|
||||||
|
@ -30,8 +28,5 @@ void main(void) {
|
||||||
|
|
||||||
_texCoord01.xy = (pos.xy + 1.0) * 0.5;
|
_texCoord01.xy = (pos.xy + 1.0) * 0.5;
|
||||||
|
|
||||||
_texCoord01.xy *= texcoordFrameTransform.zw;
|
|
||||||
_texCoord01.xy += texcoordFrameTransform.xy;
|
|
||||||
|
|
||||||
gl_Position = pos;
|
gl_Position = pos;
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,6 +104,7 @@ const uint16_t ENTITY_COLLISION_MASK_DEFAULT =
|
||||||
USER_COLLISION_GROUP_OTHER_AVATAR;
|
USER_COLLISION_GROUP_OTHER_AVATAR;
|
||||||
|
|
||||||
const uint16_t USER_COLLISION_MASK_AVATARS = USER_COLLISION_GROUP_MY_AVATAR | USER_COLLISION_GROUP_OTHER_AVATAR;
|
const uint16_t USER_COLLISION_MASK_AVATARS = USER_COLLISION_GROUP_MY_AVATAR | USER_COLLISION_GROUP_OTHER_AVATAR;
|
||||||
|
const uint16_t USER_COLLISION_MASK_ENTITIES = USER_COLLISION_GROUP_STATIC | USER_COLLISION_GROUP_DYNAMIC | USER_COLLISION_GROUP_KINEMATIC;
|
||||||
|
|
||||||
const int32_t NUM_USER_COLLISION_GROUPS = 5;
|
const int32_t NUM_USER_COLLISION_GROUPS = 5;
|
||||||
|
|
||||||
|
|
|
@ -110,7 +110,7 @@ Script.include("/~/system/libraries/controllers.js");
|
||||||
SEAT: 'seat' // The current target is a seat
|
SEAT: 'seat' // The current target is a seat
|
||||||
};
|
};
|
||||||
|
|
||||||
var speed = 12.0;
|
var speed = 9.3;
|
||||||
var accelerationAxis = {x: 0.0, y: -5.0, z: 0.0};
|
var accelerationAxis = {x: 0.0, y: -5.0, z: 0.0};
|
||||||
|
|
||||||
function Teleporter(hand) {
|
function Teleporter(hand) {
|
||||||
|
|
Loading…
Reference in a new issue