Merging with Master

This commit is contained in:
samcake 2015-12-10 16:55:30 -08:00
commit 3728a9c6f0
59 changed files with 789 additions and 790 deletions

View file

@ -416,27 +416,6 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
return;
}
/* TODO: Looking for a way to prevent locking and encoding a tree that is not
// going to result in any packets being sent...
//
// If our node is root, and the root hasn't changed, and our view hasn't changed,
// and we've already seen at least one duplicate packet, then we probably don't need
// to lock the tree and encode, because the result should be that no bytes will be
// encoded, and this will be a duplicate packet from the last one we sent...
OctreeElementPointer root = _myServer->getOctree()->getRoot();
bool skipEncode = false;
if (
(subTree == root)
&& (nodeData->getLastRootTimestamp() == root->getLastChanged())
&& !viewFrustumChanged
&& (nodeData->getDuplicatePacketCount() > 0)
) {
qDebug() << "is root, root not changed, view not changed, already seen a duplicate!"
<< "Can we skip it?";
skipEncode = true;
}
*/
float octreeSizeScale = nodeData->getOctreeSizeScale();
int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust();
@ -471,20 +450,9 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
// the packet and send it
completedScene = nodeData->elementBag.isEmpty();
// if we're trying to fill a full size packet, then we use this logic to determine if we have a DIDNT_FIT case.
if (_packetData.getTargetSize() == MAX_OCTREE_PACKET_DATA_SIZE) {
if (_packetData.hasContent() && bytesWritten == 0 &&
params.stopReason == EncodeBitstreamParams::DIDNT_FIT) {
lastNodeDidntFit = true;
}
} else {
// in compressed mode and we are trying to pack more... and we don't care if the _packetData has
// content or not... because in this case even if we were unable to pack any data, we want to drop
// below to our sendNow logic, but we do want to track that we attempted to pack extra
if (params.stopReason == EncodeBitstreamParams::DIDNT_FIT) {
lastNodeDidntFit = true;
extraPackingAttempts++;
if (bytesWritten == 0 && params.stopReason == EncodeBitstreamParams::DIDNT_FIT) {
lastNodeDidntFit = true;
}
}
nodeData->stats.encodeStopped();
@ -517,7 +485,6 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
}
nodeData->writeToPacket(_packetData.getFinalizedData(), _packetData.getFinalizedSize());
extraPackingAttempts = 0;
quint64 compressAndWriteEnd = usecTimestampNow();
compressAndWriteElapsedUsec = (float)(compressAndWriteEnd - compressAndWriteStart);
}
@ -526,8 +493,8 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
// the packet doesn't have enough space to bother attempting to pack more...
bool sendNow = true;
if (nodeData->getAvailable() >= MINIMUM_ATTEMPT_MORE_PACKING &&
extraPackingAttempts <= REASONABLE_NUMBER_OF_PACKING_ATTEMPTS) {
if (!completedScene && (nodeData->getAvailable() >= MINIMUM_ATTEMPT_MORE_PACKING &&
extraPackingAttempts <= REASONABLE_NUMBER_OF_PACKING_ATTEMPTS)) {
sendNow = false; // try to pack more
}
@ -539,6 +506,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
packetSendingElapsedUsec = (float)(packetSendingEnd - packetSendingStart);
targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE);
extraPackingAttempts = 0;
} else {
// If we're in compressed mode, then we want to see if we have room for more in this wire packet.
// but we've finalized the _packetData, so we want to start a new section, we will do that by

View file

@ -128,7 +128,6 @@ function setUp() {
blue: 255
},
lifespan: 5.0,
visible: false,
locked: false,
isEmitting: true,
lifetime: 3600 // 1 hour; just in case

View file

@ -100,6 +100,15 @@ else()
add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM})
endif()
# These are external plugins, but we need to do the 'add dependency' here so that their
# binary directories get added to the fixup path
add_dependency_external_projects(sixense)
add_dependency_external_projects(sdl2)
if (WIN32)
add_dependency_external_projects(OpenVR)
endif()
# disable /OPT:REF and /OPT:ICF for the Debug builds
# This will prevent the following linker warnings
# LINK : warning LNK4075: ignoring '/INCREMENTAL' due to '/OPT:ICF' specification

View file

@ -352,6 +352,7 @@ bool setupEssentials(int& argc, char** argv) {
DependencyManager::set<UserInputMapper>();
DependencyManager::set<controller::ScriptingInterface, ControllerScriptingInterface>();
DependencyManager::set<InterfaceParentFinder>();
DependencyManager::set<EntityTreeRenderer>(true, qApp, qApp);
return true;
}
@ -371,7 +372,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
_frameCount(0),
_fps(60.0f),
_physicsEngine(new PhysicsEngine(Vectors::ZERO)),
_entities(true, this, this),
_entityClipboardRenderer(false, this, this),
_entityClipboard(new EntityTree()),
_lastQueriedTime(usecTimestampNow()),
@ -864,7 +864,7 @@ void Application::cleanupBeforeQuit() {
}
_keyboardFocusHighlight = nullptr;
_entities.clear(); // this will allow entity scripts to properly shutdown
getEntities()->clear(); // this will allow entity scripts to properly shutdown
auto nodeList = DependencyManager::get<NodeList>();
@ -875,7 +875,7 @@ void Application::cleanupBeforeQuit() {
// tell the packet receiver we're shutting down, so it can drop packets
nodeList->getPacketReceiver().setShouldDropPackets(true);
_entities.shutdown(); // tell the entities system we're shutting down, so it will stop running scripts
getEntities()->shutdown(); // tell the entities system we're shutting down, so it will stop running scripts
ScriptEngine::stopAllScripts(this); // stop all currently running global scripts
// first stop all timers directly or by invokeMethod
@ -921,7 +921,7 @@ void Application::emptyLocalCache() {
}
Application::~Application() {
EntityTreePointer tree = _entities.getTree();
EntityTreePointer tree = getEntities()->getTree();
tree->setSimulation(NULL);
_octreeProcessor.terminate();
@ -1976,7 +1976,7 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
event->buttons(), event->modifiers());
_entities.mouseMoveEvent(&mappedEvent, deviceID);
getEntities()->mouseMoveEvent(&mappedEvent, deviceID);
_controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent, deviceID); // send events to any registered scripts
// if one of our scripts have asked to capture this event, then stop processing it
@ -2002,7 +2002,7 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
event->buttons(), event->modifiers());
if (!_aboutToQuit) {
_entities.mousePressEvent(&mappedEvent, deviceID);
getEntities()->mousePressEvent(&mappedEvent, deviceID);
}
_controllerScriptingInterface->emitMousePressEvent(&mappedEvent); // send events to any registered scripts
@ -2047,7 +2047,7 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
event->buttons(), event->modifiers());
if (!_aboutToQuit) {
_entities.mouseReleaseEvent(&mappedEvent, deviceID);
getEntities()->mouseReleaseEvent(&mappedEvent, deviceID);
}
_controllerScriptingInterface->emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts
@ -2370,7 +2370,7 @@ void Application::calibrateEyeTracker5Points() {
bool Application::exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs) {
QVector<EntityItemPointer> entities;
auto entityTree = _entities.getTree();
auto entityTree = getEntities()->getTree();
auto exportTree = std::make_shared<EntityTree>();
exportTree->createRootElement();
@ -2414,7 +2414,7 @@ bool Application::exportEntities(const QString& filename, const QVector<EntityIt
bool Application::exportEntities(const QString& filename, float x, float y, float z, float scale) {
QVector<EntityItemPointer> entities;
_entities.getTree()->findEntities(AACube(glm::vec3(x, y, z), scale), entities);
getEntities()->getTree()->findEntities(AACube(glm::vec3(x, y, z), scale), entities);
if (entities.size() > 0) {
glm::vec3 root(x, y, z);
@ -2482,7 +2482,7 @@ bool Application::importEntities(const QString& urlOrFilename) {
}
QVector<EntityItemID> Application::pasteEntities(float x, float y, float z) {
return _entityClipboard->sendEntities(&_entityEditSender, _entities.getTree(), x, y, z);
return _entityClipboard->sendEntities(&_entityEditSender, getEntities()->getTree(), x, y, z);
}
void Application::initDisplay() {
@ -2521,13 +2521,13 @@ void Application::init() {
// fire off an immediate domain-server check in now that settings are loaded
DependencyManager::get<NodeList>()->sendDomainServerCheckIn();
_entities.init();
_entities.setViewFrustum(getViewFrustum());
getEntities()->init();
getEntities()->setViewFrustum(getViewFrustum());
ObjectMotionState::setShapeManager(&_shapeManager);
_physicsEngine->init();
EntityTreePointer tree = _entities.getTree();
EntityTreePointer tree = getEntities()->getTree();
_entitySimulation.init(tree, _physicsEngine, &_entityEditSender);
tree->setSimulation(&_entitySimulation);
@ -2535,11 +2535,11 @@ void Application::init() {
// connect the _entityCollisionSystem to our EntityTreeRenderer since that's what handles running entity scripts
connect(&_entitySimulation, &EntitySimulation::entityCollisionWithEntity,
&_entities, &EntityTreeRenderer::entityCollisionWithEntity);
getEntities(), &EntityTreeRenderer::entityCollisionWithEntity);
// connect the _entities (EntityTreeRenderer) to our script engine's EntityScriptingInterface for firing
// of events related clicking, hovering over, and entering entities
_entities.connectSignalsToSlots(entityScriptingInterface.data());
getEntities()->connectSignalsToSlots(entityScriptingInterface.data());
_entityClipboardRenderer.init();
_entityClipboardRenderer.setViewFrustum(getViewFrustum());
@ -2888,19 +2888,19 @@ void Application::update(float deltaTime) {
_avatarUpdate->synchronousProcess();
if (true || _physicsEnabled) {
if (_physicsEnabled) {
PerformanceTimer perfTimer("physics");
static VectorOfMotionStates motionStates;
_entitySimulation.getObjectsToDelete(motionStates);
_physicsEngine->deleteObjects(motionStates);
_entities.getTree()->withWriteLock([&] {
getEntities()->getTree()->withWriteLock([&] {
_entitySimulation.getObjectsToAdd(motionStates);
_physicsEngine->addObjects(motionStates);
});
_entities.getTree()->withWriteLock([&] {
getEntities()->getTree()->withWriteLock([&] {
_entitySimulation.getObjectsToChange(motionStates);
VectorOfMotionStates stillNeedChange = _physicsEngine->changeObjects(motionStates);
_entitySimulation.setObjectsToChange(stillNeedChange);
@ -2918,12 +2918,12 @@ void Application::update(float deltaTime) {
myAvatar->prepareForPhysicsSimulation();
_entities.getTree()->withWriteLock([&] {
getEntities()->getTree()->withWriteLock([&] {
_physicsEngine->stepSimulation();
});
if (_physicsEngine->hasOutgoingChanges()) {
_entities.getTree()->withWriteLock([&] {
getEntities()->getTree()->withWriteLock([&] {
_entitySimulation.handleOutgoingChanges(_physicsEngine->getOutgoingChanges(), _physicsEngine->getSessionID());
avatarManager->handleOutgoingChanges(_physicsEngine->getOutgoingChanges());
});
@ -2938,12 +2938,13 @@ void Application::update(float deltaTime) {
// Collision events (and their scripts) must not be handled when we're locked, above. (That would risk
// deadlock.)
_entitySimulation.handleCollisionEvents(collisionEvents);
// NOTE: the _entities.update() call below will wait for lock
// NOTE: the getEntities()->update() call below will wait for lock
// and will simulate entity motion (the EntityTree has been given an EntitySimulation).
_entities.update(); // update the models...
getEntities()->update(); // update the models...
}
myAvatar->harvestResultsFromPhysicsSimulation();
myAvatar->simulateAttachments(deltaTime);
}
}
@ -3236,6 +3237,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
bool Application::isHMDMode() const {
return getActiveDisplayPlugin()->isHmd();
}
float Application::getTargetFrameRate() { return getActiveDisplayPlugin()->getTargetFrameRate(); }
QRect Application::getDesirableApplicationGeometry() {
QRect applicationGeometry = getWindow()->geometry();
@ -3762,7 +3764,7 @@ void Application::clearDomainOctreeDetails() {
});
// reset the model renderer
_entities.clear();
getEntities()->clear();
}
void Application::domainChanged(const QString& domainHostname) {
@ -3884,7 +3886,7 @@ bool Application::nearbyEntitiesAreReadyForPhysics() {
// Someone logs in close to the table. They receive information about the items on the table before they
// receive information about the table. The items are very close to the avatar's capsule, so they become
// activated in bullet. This causes them to fall to the floor, because the table's shape isn't yet in bullet.
EntityTreePointer entityTree = _entities.getTree();
EntityTreePointer entityTree = getEntities()->getTree();
if (!entityTree) {
return false;
}
@ -3897,6 +3899,9 @@ bool Application::nearbyEntitiesAreReadyForPhysics() {
foreach (EntityItemPointer entity, entities) {
if (!entity->isReadyToComputeShape()) {
static QString repeatedMessage =
LogHandler::getInstance().addRepeatedMessageRegex("Physics disabled until entity loads: .*");
qCDebug(interfaceapp) << "Physics disabled until entity loads: " << entity->getID() << entity->getName();
return false;
}
}
@ -3948,11 +3953,21 @@ int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer
});
});
if (!_physicsEnabled && nearbyEntitiesAreReadyForPhysics()) {
// These stats packets are sent in between full sends of a scene.
// We keep physics disabled until we've recieved a full scene and everything near the avatar in that
// scene is ready to compute its collision shape.
_physicsEnabled = true;
if (!_physicsEnabled) {
if (nearbyEntitiesAreReadyForPhysics()) {
// These stats packets are sent in between full sends of a scene.
// We keep physics disabled until we've recieved a full scene and everything near the avatar in that
// scene is ready to compute its collision shape.
_physicsEnabled = true;
getMyAvatar()->updateMotionBehaviorFromMenu();
} else {
auto characterController = getMyAvatar()->getCharacterController();
if (characterController) {
// if we have a character controller, disable it here so the avatar doesn't get stuck due to
// a non-loading collision hull.
characterController->setEnabled(false);
}
}
}
return statsMessageLength;
@ -4015,7 +4030,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
// we can use the same ones from the application.
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
entityScriptingInterface->setPacketSender(&_entityEditSender);
entityScriptingInterface->setEntityTree(_entities.getTree());
entityScriptingInterface->setEntityTree(getEntities()->getTree());
// AvatarManager has some custom types
AvatarManager::registerMetaTypes(scriptEngine);
@ -4036,7 +4051,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("Clipboard", clipboardScriptable);
connect(scriptEngine, SIGNAL(finished(const QString&)), clipboardScriptable, SLOT(deleteLater()));
connect(scriptEngine, SIGNAL(finished(const QString&)), this, SLOT(scriptFinished(const QString&)));
connect(scriptEngine, &ScriptEngine::finished, this, &Application::scriptFinished, Qt::DirectConnection);
connect(scriptEngine, SIGNAL(loadScript(const QString&, bool)), this, SLOT(loadScript(const QString&, bool)));
connect(scriptEngine, SIGNAL(reloadScript(const QString&, bool)), this, SLOT(reloadScript(const QString&, bool)));
@ -4282,10 +4297,13 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser
QUrl scriptUrl(scriptFilename);
const QString& scriptURLString = scriptUrl.toString();
if (_scriptEnginesHash.contains(scriptURLString) && loadScriptFromEditor
&& !_scriptEnginesHash[scriptURLString]->isFinished()) {
{
QReadLocker lock(&_scriptEnginesHashLock);
if (_scriptEnginesHash.contains(scriptURLString) && loadScriptFromEditor
&& !_scriptEnginesHash[scriptURLString]->isFinished()) {
return _scriptEnginesHash[scriptURLString];
return _scriptEnginesHash[scriptURLString];
}
}
ScriptEngine* scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface);
@ -4325,7 +4343,11 @@ void Application::reloadScript(const QString& scriptName, bool isUserLoaded) {
void Application::handleScriptEngineLoaded(const QString& scriptFilename) {
ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(sender());
_scriptEnginesHash.insertMulti(scriptFilename, scriptEngine);
{
QWriteLocker lock(&_scriptEnginesHashLock);
_scriptEnginesHash.insertMulti(scriptFilename, scriptEngine);
}
_runningScriptsWidget->setRunningScripts(getRunningScripts());
UserActivityLogger::getInstance().loadedScript(scriptFilename);
@ -4340,55 +4362,88 @@ void Application::handleScriptLoadError(const QString& scriptFilename) {
QMessageBox::warning(getWindow(), "Error Loading Script", scriptFilename + " failed to load.");
}
void Application::scriptFinished(const QString& scriptName) {
const QString& scriptURLString = QUrl(scriptName).toString();
QHash<QString, ScriptEngine*>::iterator it = _scriptEnginesHash.find(scriptURLString);
if (it != _scriptEnginesHash.end()) {
_scriptEnginesHash.erase(it);
_runningScriptsWidget->scriptStopped(scriptName);
_runningScriptsWidget->setRunningScripts(getRunningScripts());
QStringList Application::getRunningScripts() {
QReadLocker lock(&_scriptEnginesHashLock);
return _scriptEnginesHash.keys();
}
ScriptEngine* Application::getScriptEngine(const QString& scriptHash) {
QReadLocker lock(&_scriptEnginesHashLock);
return _scriptEnginesHash.value(scriptHash, nullptr);
}
void Application::scriptFinished(const QString& scriptName, ScriptEngine* engine) {
bool removed = false;
{
QWriteLocker lock(&_scriptEnginesHashLock);
const QString& scriptURLString = QUrl(scriptName).toString();
for (auto it = _scriptEnginesHash.find(scriptURLString); it != _scriptEnginesHash.end(); ++it) {
if (it.value() == engine) {
_scriptEnginesHash.erase(it);
removed = true;
break;
}
}
}
if (removed) {
postLambdaEvent([this, scriptName]() {
_runningScriptsWidget->scriptStopped(scriptName);
_runningScriptsWidget->setRunningScripts(getRunningScripts());
});
}
}
void Application::stopAllScripts(bool restart) {
if (restart) {
// Delete all running scripts from cache so that they are re-downloaded when they are restarted
auto scriptCache = DependencyManager::get<ScriptCache>();
for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
{
QReadLocker lock(&_scriptEnginesHashLock);
if (restart) {
// Delete all running scripts from cache so that they are re-downloaded when they are restarted
auto scriptCache = DependencyManager::get<ScriptCache>();
for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
it != _scriptEnginesHash.constEnd(); it++) {
if (!it.value()->isFinished()) {
scriptCache->deleteScript(it.key());
if (!it.value()->isFinished()) {
scriptCache->deleteScript(it.key());
}
}
}
}
// Stop and possibly restart all currently running scripts
for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
it != _scriptEnginesHash.constEnd(); it++) {
if (it.value()->isFinished()) {
continue;
// Stop and possibly restart all currently running scripts
for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
it != _scriptEnginesHash.constEnd(); it++) {
if (it.value()->isFinished()) {
continue;
}
if (restart && it.value()->isUserLoaded()) {
connect(it.value(), &ScriptEngine::finished, this, [this](QString scriptName, ScriptEngine* engine) {
reloadScript(scriptName);
});
}
QMetaObject::invokeMethod(it.value(), "stop");
//it.value()->stop();
qCDebug(interfaceapp) << "stopping script..." << it.key();
}
if (restart && it.value()->isUserLoaded()) {
connect(it.value(), SIGNAL(finished(const QString&)), SLOT(reloadScript(const QString&)));
}
it.value()->stop();
qCDebug(interfaceapp) << "stopping script..." << it.key();
}
getMyAvatar()->clearScriptableSettings();
}
bool Application::stopScript(const QString& scriptHash, bool restart) {
bool stoppedScript = false;
if (_scriptEnginesHash.contains(scriptHash)) {
ScriptEngine* scriptEngine = _scriptEnginesHash[scriptHash];
if (restart) {
auto scriptCache = DependencyManager::get<ScriptCache>();
scriptCache->deleteScript(QUrl(scriptHash));
connect(scriptEngine, SIGNAL(finished(const QString&)), SLOT(reloadScript(const QString&)));
{
QReadLocker lock(&_scriptEnginesHashLock);
if (_scriptEnginesHash.contains(scriptHash)) {
ScriptEngine* scriptEngine = _scriptEnginesHash[scriptHash];
if (restart) {
auto scriptCache = DependencyManager::get<ScriptCache>();
scriptCache->deleteScript(QUrl(scriptHash));
connect(scriptEngine, &ScriptEngine::finished, this, [this](QString scriptName, ScriptEngine* engine) {
reloadScript(scriptName);
});
}
scriptEngine->stop();
stoppedScript = true;
qCDebug(interfaceapp) << "stopping script..." << scriptHash;
}
scriptEngine->stop();
stoppedScript = true;
qCDebug(interfaceapp) << "stopping script..." << scriptHash;
}
if (_scriptEnginesHash.empty()) {
getMyAvatar()->clearScriptableSettings();
@ -4407,6 +4462,7 @@ void Application::reloadOneScript(const QString& scriptName) {
}
void Application::loadDefaultScripts() {
QReadLocker lock(&_scriptEnginesHashLock);
if (!_scriptEnginesHash.contains(DEFAULT_SCRIPTS_JS_URL)) {
loadScript(DEFAULT_SCRIPTS_JS_URL);
}

View file

@ -136,7 +136,7 @@ public:
const ViewFrustum* getDisplayViewFrustum() const;
ViewFrustum* getShadowViewFrustum() { return &_shadowViewFrustum; }
const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; }
EntityTreeRenderer* getEntities() { return &_entities; }
EntityTreeRenderer* getEntities() { return DependencyManager::get<EntityTreeRenderer>().data(); }
QUndoStack* getUndoStack() { return &_undoStack; }
MainWindow* getWindow() { return _window; }
EntityTreePointer getEntityClipboard() { return _entityClipboard; }
@ -160,9 +160,7 @@ public:
uint32_t getFrameCount() { return _frameCount; }
float getFps() const { return _fps; }
float const HMD_TARGET_FRAME_RATE = 75.0f;
float const DESKTOP_TARGET_FRAME_RATE = 60.0f;
float getTargetFrameRate() { return isHMDMode() ? HMD_TARGET_FRAME_RATE : DESKTOP_TARGET_FRAME_RATE; }
float getTargetFrameRate(); // frames/second
float getLastInstanteousFps() const { return _lastInstantaneousFps; }
float getLastUnsynchronizedFps() const { return _lastUnsynchronizedFps; }
@ -199,8 +197,8 @@ public:
NodeToJurisdictionMap& getEntityServerJurisdictions() { return _entityServerJurisdictions; }
QStringList getRunningScripts() { return _scriptEnginesHash.keys(); }
ScriptEngine* getScriptEngine(const QString& scriptHash) { return _scriptEnginesHash.value(scriptHash, NULL); }
QStringList getRunningScripts();
ScriptEngine* getScriptEngine(const QString& scriptHash);
float getRenderResolutionScale() const;
@ -333,7 +331,7 @@ private slots:
void loadSettings();
void saveSettings();
void scriptFinished(const QString& scriptName);
void scriptFinished(const QString& scriptName, ScriptEngine* engine);
void saveScripts();
void reloadScript(const QString& scriptName, bool isUserLoaded = true);
@ -446,7 +444,6 @@ private:
PhysicalEntitySimulation _entitySimulation;
PhysicsEnginePointer _physicsEngine;
EntityTreeRenderer _entities;
EntityTreeRenderer _entityClipboardRenderer;
EntityTreePointer _entityClipboard;
@ -501,6 +498,7 @@ private:
TouchEvent _lastTouchEvent;
QReadWriteLock _scriptEnginesHashLock;
RunningScriptsWidget* _runningScriptsWidget;
QHash<QString, ScriptEngine*> _scriptEnginesHash;
bool _runningScriptsWidgetWasVisible;

View file

@ -200,7 +200,6 @@ void Avatar::simulate(float deltaTime) {
PerformanceTimer perfTimer("skeleton");
_skeletonModel.getRig()->copyJointsFromJointData(_jointData);
_skeletonModel.simulate(deltaTime, _hasNewJointRotations || _hasNewJointTranslations);
simulateAttachments(deltaTime);
locationChanged(); // joints changed, so if there are any children, update them.
_hasNewJointRotations = false;
_hasNewJointTranslations = false;

View file

@ -69,6 +69,7 @@ public:
void init();
void simulate(float deltaTime);
void simulateAttachments(float deltaTime);
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPosition);
@ -87,7 +88,6 @@ public:
bool isInitialized() const { return _initialized; }
SkeletonModel& getSkeletonModel() { return _skeletonModel; }
const SkeletonModel& getSkeletonModel() const { return _skeletonModel; }
const QVector<Model*>& getAttachmentModels() const { return _attachmentModels; }
glm::vec3 getChestPosition() const;
float getAvatarScale() const { return getScale().y; }
const Head* getHead() const { return static_cast<const Head*>(_headData); }
@ -217,8 +217,6 @@ protected:
virtual bool shouldRenderHead(const RenderArgs* renderArgs) const;
virtual void fixupModelsInScene();
void simulateAttachments(float deltaTime);
virtual void updateJointMappings();
render::ItemID _renderItemID;

View file

@ -145,13 +145,19 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
PerformanceTimer perfTimer("otherAvatars");
_renderDistanceController.setMeasuredValueSetpoint(qApp->getTargetFrameRate()); // No problem updating in flight.
// The PID controller raises the controlled value when the measured value goes up.
// The measured value is frame rate. When the controlled value (1 / render cutoff distance)
// goes up, the render cutoff distance gets closer, the number of rendered avatars is less, and frame rate
// goes up.
const float deduced = qApp->getLastUnsynchronizedFps();
const float distance = 1.0f / _renderDistanceController.update(deduced, deltaTime);
float distance;
if (!qApp->isThrottleRendering()) {
_renderDistanceController.setMeasuredValueSetpoint(qApp->getTargetFrameRate()); // No problem updating in flight.
// The PID controller raises the controlled value when the measured value goes up.
// The measured value is frame rate. When the controlled value (1 / render cutoff distance)
// goes up, the render cutoff distance gets closer, the number of rendered avatars is less, and frame rate
// goes up.
const float deduced = qApp->getLastUnsynchronizedFps();
distance = 1.0f / _renderDistanceController.update(deduced, deltaTime);
} else {
// Here we choose to just use the maximum render cutoff distance if throttled.
distance = 1.0f / _renderDistanceController.getControlledValueLowLimit();
}
_renderDistanceAverage.updateAverage(distance);
_renderDistance = _renderDistanceAverage.getAverage();
int renderableCount = 0;

View file

@ -323,11 +323,6 @@ void MyAvatar::simulate(float deltaTime) {
return;
}
{
PerformanceTimer perfTimer("attachments");
simulateAttachments(deltaTime);
}
{
PerformanceTimer perfTimer("joints");
// copy out the skeleton joints from the model

View file

@ -20,9 +20,6 @@
#include "Application.h"
#include "devices/MotionTracker.h"
// TODO: this needs to be removed, as well as any related controller-specific information
#include <input-plugins/SixenseManager.h>
void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) {
if (event->type() == HFActionEvent::startType()) {
emit actionStartEvent(static_cast<HFActionEvent&>(*event));

View file

@ -26,7 +26,6 @@
#include "Tooltip.h"
#include "Application.h"
#include <input-plugins/SixenseManager.h> // TODO: any references to sixense should be removed here
#include <controllers/InputDevice.h>

View file

@ -16,7 +16,6 @@
#include <avatar/AvatarManager.h>
#include <devices/DdeFaceTracker.h>
#include <devices/Faceshift.h>
#include <input-plugins/SixenseManager.h> // TODO: This should be replaced with InputDevice/InputPlugin, or something similar
#include <NetworkingConstants.h>
#include "Application.h"

View file

@ -47,7 +47,7 @@ const QString AvatarData::FRAME_NAME = "com.highfidelity.recording.AvatarData";
static std::once_flag frameTypeRegistration;
AvatarData::AvatarData() :
SpatiallyNestable(NestableTypes::Avatar, QUuid()),
SpatiallyNestable(NestableType::Avatar, QUuid()),
_handPosition(0.0f),
_targetScale(1.0f),
_handState(0),

View file

@ -83,19 +83,18 @@ void Basic2DWindowOpenGLDisplayPlugin::internalPresent() {
}
WindowOpenGLDisplayPlugin::internalPresent();
}
const uint32_t THROTTLED_FRAMERATE = 15;
int Basic2DWindowOpenGLDisplayPlugin::getDesiredInterval() const {
static const int THROTTLED_PAINT_TIMER_DELAY_MS = MSECS_PER_SECOND / 15;
static const int ULIMIITED_PAINT_TIMER_DELAY_MS = 1;
int result = ULIMIITED_PAINT_TIMER_DELAY_MS;
if (_isThrottled) {
result = THROTTLED_PAINT_TIMER_DELAY_MS;
}
if (0 != _framerateTarget) {
result = MSECS_PER_SECOND / _framerateTarget;
} else if (_isThrottled) {
// This test wouldn't be necessary if we could depend on updateFramerate setting _framerateTarget.
// Alas, that gets complicated: isThrottled() is const and other stuff depends on it.
result = MSECS_PER_SECOND / THROTTLED_FRAMERATE;
}
qDebug() << "New interval " << result;
return result;
}
@ -112,7 +111,6 @@ bool Basic2DWindowOpenGLDisplayPlugin::isThrottled() const {
return shouldThrottle;
}
void Basic2DWindowOpenGLDisplayPlugin::updateFramerate() {
QAction* checkedFramerate{ nullptr };
foreach(auto action, _framerateActions) {
@ -134,11 +132,12 @@ void Basic2DWindowOpenGLDisplayPlugin::updateFramerate() {
} else if (FRAMERATE_30 == actionText) {
_framerateTarget = 30;
}
}
} else if (_isThrottled) {
_framerateTarget = THROTTLED_FRAMERATE;
}
int newInterval = getDesiredInterval();
qDebug() << newInterval;
_timer.start(getDesiredInterval());
_timer.start(newInterval);
}
// FIXME target the screen the window is currently on

View file

@ -9,6 +9,8 @@
#include "WindowOpenGLDisplayPlugin.h"
const float TARGET_FRAMERATE_Basic2DWindowOpenGL = 60.0f;
class QScreen;
class QAction;
@ -18,6 +20,8 @@ class Basic2DWindowOpenGLDisplayPlugin : public WindowOpenGLDisplayPlugin {
public:
virtual const QString & getName() const override;
virtual float getTargetFrameRate() override { return _framerateTarget ? (float) _framerateTarget : TARGET_FRAMERATE_Basic2DWindowOpenGL; }
virtual void activate() override;
virtual void submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) override;

View file

@ -30,7 +30,7 @@ class ZoneEntityItem;
// Generic client side Octree renderer class.
class EntityTreeRenderer : public OctreeRenderer, public EntityItemFBXService {
class EntityTreeRenderer : public OctreeRenderer, public EntityItemFBXService, public Dependency {
Q_OBJECT
public:
EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,

View file

@ -12,12 +12,14 @@
#include <glm/gtx/quaternion.hpp>
#include <QJsonDocument>
#include <QtCore/QThread>
#include <AbstractViewStateInterface.h>
#include <DeferredLightingEffect.h>
#include <Model.h>
#include <PerfStat.h>
#include <render/Scene.h>
#include <DependencyManager.h>
#include "EntityTreeRenderer.h"
#include "EntitiesRendererLogging.h"
@ -44,6 +46,32 @@ RenderableModelEntityItem::~RenderableModelEntityItem() {
}
}
void RenderableModelEntityItem::setModelURL(const QString& url) {
auto& currentURL = getParsedModelURL();
ModelEntityItem::setModelURL(url);
if (currentURL != getParsedModelURL() || !_model) {
EntityTreePointer tree = getTree();
if (tree) {
QMetaObject::invokeMethod(tree.get(), "callLoader", Qt::QueuedConnection, Q_ARG(EntityItemID, getID()));
}
}
}
void RenderableModelEntityItem::loader() {
_needsModelReload = true;
EntityTreeRenderer* renderer = DependencyManager::get<EntityTreeRenderer>().data();
assert(renderer);
if (!_model || _needsModelReload) {
PerformanceTimer perfTimer("getModel");
getModel(renderer);
}
if (_model) {
_model->setURL(getParsedModelURL());
_model->setCollisionModelURL(QUrl(getCompoundShapeURL()));
}
}
void RenderableModelEntityItem::setDimensions(const glm::vec3& value) {
_dimensionsInitialized = true;
ModelEntityItem::setDimensions(value);
@ -223,7 +251,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
// check if the URL has changed
auto& currentURL = getParsedModelURL();
if (currentURL != _model->getURL()) {
qDebug().noquote() << "Updating model URL: " << currentURL.toDisplayString();
qCDebug(entitiesrenderer).noquote() << "Updating model URL: " << currentURL.toDisplayString();
_model->setURL(currentURL);
}
@ -318,7 +346,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) {
Model* result = NULL;
if (!renderer) {
return result;
}
@ -340,7 +368,8 @@ Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) {
// if we have a previously allocated model, but its URL doesn't match
// then we need to let our renderer update our model for us.
if (_model && QUrl(getModelURL()) != _model->getURL()) {
if (_model && (QUrl(getModelURL()) != _model->getURL() ||
QUrl(getCompoundShapeURL()) != _model->getCollisionURL())) {
result = _model = _myRenderer->updateModel(_model, getModelURL(), getCompoundShapeURL());
_needsInitialSimulation = true;
} else if (!_model) { // if we don't yet have a model, then we want our renderer to allocate one
@ -403,23 +432,34 @@ bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& ori
}
void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) {
auto currentCompoundShapeURL = getCompoundShapeURL();
ModelEntityItem::setCompoundShapeURL(url);
if (_model) {
_model->setCollisionModelURL(QUrl(url));
if (getCompoundShapeURL() != currentCompoundShapeURL || !_model) {
EntityTreePointer tree = getTree();
if (tree) {
QMetaObject::invokeMethod(tree.get(), "callLoader", Qt::QueuedConnection, Q_ARG(EntityItemID, getID()));
}
}
}
bool RenderableModelEntityItem::isReadyToComputeShape() {
ShapeType type = getShapeType();
if (type == SHAPE_TYPE_COMPOUND) {
if (type == SHAPE_TYPE_COMPOUND) {
if (!_model) {
EntityTreePointer tree = getTree();
if (tree) {
QMetaObject::invokeMethod(tree.get(), "callLoader", Qt::QueuedConnection, Q_ARG(EntityItemID, getID()));
}
return false; // hmm...
}
if (_needsInitialSimulation) {
// the _model's offset will be wrong until _needsInitialSimulation is false
return false;
PerformanceTimer perfTimer("_model->simulate");
_model->simulate(0.0f);
_needsInitialSimulation = false;
}
assert(!_model->getCollisionURL().isEmpty());

View file

@ -29,7 +29,8 @@ public:
virtual ~RenderableModelEntityItem();
virtual void setDimensions(const glm::vec3& value) override;
virtual void setModelURL(const QString& url);
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
virtual bool setProperties(const EntityItemProperties& properties) override;
virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
@ -71,6 +72,8 @@ public:
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override;
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override;
virtual void loader() override;
private:
void remapTextures();

View file

@ -23,43 +23,73 @@
#include "untextured_particle_frag.h"
#include "textured_particle_vert.h"
#include "textured_particle_frag.h"
#include "textured_particle_alpha_discard_frag.h"
class ParticlePayload {
class ParticlePayloadData {
public:
typedef render::Payload<ParticlePayload> Payload;
typedef Payload::DataPointer Pointer;
typedef RenderableParticleEffectEntityItem::Vertex Vertex;
static const size_t VERTEX_PER_PARTICLE = 4;
ParticlePayload(EntityItemPointer entity) :
_entity(entity),
_vertexFormat(std::make_shared<gpu::Stream::Format>()),
_vertexBuffer(std::make_shared<gpu::Buffer>()),
_indexBuffer(std::make_shared<gpu::Buffer>()) {
_vertexFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element::VEC3F_XYZ, 0);
_vertexFormat->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), offsetof(Vertex, uv));
_vertexFormat->setAttribute(gpu::Stream::COLOR, 0, gpu::Element::COLOR_RGBA_32, offsetof(Vertex, rgba));
template<typename T>
struct InterpolationData {
T start;
T middle;
T finish;
T spread;
};
struct ParticleUniforms {
InterpolationData<float> radius;
InterpolationData<glm::vec4> color; // rgba
float lifespan;
};
struct ParticlePrimitive {
ParticlePrimitive(glm::vec3 xyzIn, glm::vec2 uvIn) : xyz(xyzIn), uv(uvIn) {}
glm::vec3 xyz; // Position
glm::vec2 uv; // Lifetime + seed
};
using Payload = render::Payload<ParticlePayloadData>;
using Pointer = Payload::DataPointer;
using PipelinePointer = gpu::PipelinePointer;
using FormatPointer = gpu::Stream::FormatPointer;
using BufferPointer = gpu::BufferPointer;
using TexturePointer = gpu::TexturePointer;
using Format = gpu::Stream::Format;
using Buffer = gpu::Buffer;
using BufferView = gpu::BufferView;
using ParticlePrimitives = std::vector<ParticlePrimitive>;
ParticlePayloadData() {
ParticleUniforms uniforms;
_uniformBuffer = std::make_shared<Buffer>(sizeof(ParticleUniforms), (const gpu::Byte*) &uniforms);
_vertexFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element::VEC3F_XYZ,
offsetof(ParticlePrimitive, xyz), gpu::Stream::PER_INSTANCE);
_vertexFormat->setAttribute(gpu::Stream::COLOR, 0, gpu::Element::VEC2F_UV,
offsetof(ParticlePrimitive, uv), gpu::Stream::PER_INSTANCE);
}
void setPipeline(gpu::PipelinePointer pipeline) { _pipeline = pipeline; }
const gpu::PipelinePointer& getPipeline() const { return _pipeline; }
void setPipeline(PipelinePointer pipeline) { _pipeline = pipeline; }
const PipelinePointer& getPipeline() const { return _pipeline; }
const Transform& getModelTransform() const { return _modelTransform; }
void setModelTransform(const Transform& modelTransform) { _modelTransform = modelTransform; }
const AABox& getBound() const { return _bound; }
void setBound(AABox& bound) { _bound = bound; }
void setBound(const AABox& bound) { _bound = bound; }
gpu::BufferPointer getVertexBuffer() { return _vertexBuffer; }
const gpu::BufferPointer& getVertexBuffer() const { return _vertexBuffer; }
BufferPointer getParticleBuffer() { return _particleBuffer; }
const BufferPointer& getParticleBuffer() const { return _particleBuffer; }
const ParticleUniforms& getParticleUniforms() const { return _uniformBuffer.get<ParticleUniforms>(); }
ParticleUniforms& editParticleUniforms() { return _uniformBuffer.edit<ParticleUniforms>(); }
gpu::BufferPointer getIndexBuffer() { return _indexBuffer; }
const gpu::BufferPointer& getIndexBuffer() const { return _indexBuffer; }
void setTexture(gpu::TexturePointer texture) { _texture = texture; }
const gpu::TexturePointer& getTexture() const { return _texture; }
void setTexture(TexturePointer texture) { _texture = texture; }
const TexturePointer& getTexture() const { return _texture; }
bool getVisibleFlag() const { return _visibleFlag; }
void setVisibleFlag(bool visibleFlag) { _visibleFlag = visibleFlag; }
void render(RenderArgs* args) const {
assert(_pipeline);
@ -71,30 +101,29 @@ public:
}
batch.setModelTransform(_modelTransform);
batch.setUniformBuffer(0, _uniformBuffer);
batch.setInputFormat(_vertexFormat);
batch.setInputBuffer(0, _vertexBuffer, 0, sizeof(Vertex));
batch.setIndexBuffer(gpu::UINT16, _indexBuffer, 0);
batch.setInputBuffer(0, _particleBuffer, 0, sizeof(ParticlePrimitive));
auto numIndices = _indexBuffer->getSize() / sizeof(uint16_t);
batch.drawIndexed(gpu::TRIANGLES, numIndices);
auto numParticles = _particleBuffer->getSize() / sizeof(ParticlePrimitive);
batch.drawInstanced(numParticles, gpu::TRIANGLE_STRIP, VERTEX_PER_PARTICLE);
}
EntityItemPointer _entity;
protected:
Transform _modelTransform;
AABox _bound;
gpu::PipelinePointer _pipeline;
gpu::Stream::FormatPointer _vertexFormat;
gpu::BufferPointer _vertexBuffer;
gpu::BufferPointer _indexBuffer;
gpu::TexturePointer _texture;
PipelinePointer _pipeline;
FormatPointer _vertexFormat { std::make_shared<Format>() };
BufferPointer _particleBuffer { std::make_shared<Buffer>() };
BufferView _uniformBuffer;
TexturePointer _texture;
bool _visibleFlag = true;
};
namespace render {
template <>
const ItemKey payloadGetKey(const ParticlePayload::Pointer& payload) {
if (payload->_entity->getVisible()) {
const ItemKey payloadGetKey(const ParticlePayloadData::Pointer& payload) {
if (payload->getVisibleFlag()) {
return ItemKey::Builder::transparentShape();
} else {
return ItemKey::Builder().withInvisible().build();
@ -102,16 +131,15 @@ namespace render {
}
template <>
const Item::Bound payloadGetBound(const ParticlePayload::Pointer& payload) {
const Item::Bound payloadGetBound(const ParticlePayloadData::Pointer& payload) {
return payload->getBound();
}
template <>
void payloadRender(const ParticlePayload::Pointer& payload, RenderArgs* args) {
if (payload->_entity->getVisible()) {
void payloadRender(const ParticlePayloadData::Pointer& payload, RenderArgs* args) {
if (payload->getVisibleFlag()) {
payload->render(args);
}
}
}
@ -119,7 +147,7 @@ namespace render {
EntityItemPointer RenderableParticleEffectEntityItem::factory(const EntityItemID& entityID,
const EntityItemProperties& properties) {
EntityItemPointer entity{ new RenderableParticleEffectEntityItem(entityID) };
auto entity = std::make_shared<RenderableParticleEffectEntityItem>(entityID);
entity->setProperties(properties);
return entity;
}
@ -127,7 +155,7 @@ EntityItemPointer RenderableParticleEffectEntityItem::factory(const EntityItemID
RenderableParticleEffectEntityItem::RenderableParticleEffectEntityItem(const EntityItemID& entityItemID) :
ParticleEffectEntityItem(entityItemID) {
// lazy creation of particle system pipeline
if (!_untexturedPipeline && !_texturedPipeline) {
if (!_untexturedPipeline || !_texturedPipeline) {
createPipelines();
}
}
@ -135,18 +163,15 @@ RenderableParticleEffectEntityItem::RenderableParticleEffectEntityItem(const Ent
bool RenderableParticleEffectEntityItem::addToScene(EntityItemPointer self,
render::ScenePointer scene,
render::PendingChanges& pendingChanges) {
auto particlePayload =
std::shared_ptr<ParticlePayload>(new ParticlePayload(getThisPointer()));
particlePayload->setPipeline(_untexturedPipeline);
_renderItemId = scene->allocateID();
auto renderData = ParticlePayload::Pointer(particlePayload);
auto renderPayload = render::PayloadPointer(new ParticlePayload::Payload(renderData));
_scene = scene;
_renderItemId = _scene->allocateID();
auto particlePayloadData = std::make_shared<ParticlePayloadData>();
particlePayloadData->setPipeline(_untexturedPipeline);
auto renderPayload = std::make_shared<ParticlePayloadData::Payload>(particlePayloadData);
render::Item::Status::Getters statusGetters;
makeEntityItemStatusGetters(getThisPointer(), statusGetters);
renderPayload->addStatusGetters(statusGetters);
pendingChanges.resetItem(_renderItemId, renderPayload);
_scene = scene;
return true;
}
@ -174,141 +199,71 @@ void RenderableParticleEffectEntityItem::update(const quint64& now) {
updateRenderItem();
}
uint32_t toRGBA(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
return ((uint32_t)r | (uint32_t)g << 8 | (uint32_t)b << 16 | (uint32_t)a << 24);
}
class ParticleDetails {
public:
ParticleDetails(glm::vec3 position, float radius, uint32_t rgba) : position(position), radius(radius), rgba(rgba) { }
glm::vec3 position;
float radius;
uint32_t rgba;
};
static glm::vec3 zSortAxis;
static bool zSort(const ParticleDetails& rhs, const ParticleDetails& lhs) {
return glm::dot(rhs.position, ::zSortAxis) > glm::dot(lhs.position, ::zSortAxis);
}
void RenderableParticleEffectEntityItem::updateRenderItem() {
if (!_scene) {
return;
}
// make a copy of each particle's details
std::vector<ParticleDetails> particleDetails;
particleDetails.reserve(getLivingParticleCount());
for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) {
auto xcolor = _particleColors[i];
auto alpha = (uint8_t)(glm::clamp(_particleAlphas[i] * getLocalRenderAlpha(), 0.0f, 1.0f) * 255.0f);
auto rgba = toRGBA(xcolor.red, xcolor.green, xcolor.blue, alpha);
particleDetails.push_back(ParticleDetails(_particlePositions[i], _particleRadiuses[i], rgba));
if (!getVisible()) {
render::PendingChanges pendingChanges;
pendingChanges.updateItem<ParticlePayloadData>(_renderItemId, [](ParticlePayloadData& payload) {
payload.setVisibleFlag(false);
});
_scene->enqueuePendingChanges(pendingChanges);
return;
}
// sort particles back to front
// NOTE: this is view frustum might be one frame out of date.
auto frustum = AbstractViewStateInterface::instance()->getCurrentViewFrustum();
using ParticleUniforms = ParticlePayloadData::ParticleUniforms;
using ParticlePrimitive = ParticlePayloadData::ParticlePrimitive;
using ParticlePrimitives = ParticlePayloadData::ParticlePrimitives;
// No need to sort if we're doing additive blending
if (_additiveBlending != true) {
::zSortAxis = frustum->getDirection();
qSort(particleDetails.begin(), particleDetails.end(), zSort);
}
// allocate vertices
_vertices.clear();
// build vertices from particle positions and radiuses
glm::vec3 dir = frustum->getDirection();
for (auto&& particle : particleDetails) {
glm::vec3 right = glm::normalize(glm::cross(glm::vec3(0.0f, 1.0f, 0.0f), dir));
glm::vec3 up = glm::normalize(glm::cross(right, dir));
glm::vec3 upOffset = up * particle.radius;
glm::vec3 rightOffset = right * particle.radius;
// generate corners of quad aligned to face the camera.
_vertices.emplace_back(particle.position + rightOffset + upOffset, glm::vec2(1.0f, 1.0f), particle.rgba);
_vertices.emplace_back(particle.position - rightOffset + upOffset, glm::vec2(0.0f, 1.0f), particle.rgba);
_vertices.emplace_back(particle.position - rightOffset - upOffset, glm::vec2(0.0f, 0.0f), particle.rgba);
_vertices.emplace_back(particle.position + rightOffset - upOffset, glm::vec2(1.0f, 0.0f), particle.rgba);
// Fill in Uniforms structure
ParticleUniforms particleUniforms;
particleUniforms.radius.start = getRadiusStart();
particleUniforms.radius.middle = getParticleRadius();
particleUniforms.radius.finish = getRadiusFinish();
particleUniforms.radius.spread = getRadiusSpread();
particleUniforms.color.start = toGlm(getColorStart(), getAlphaStart());
particleUniforms.color.middle = toGlm(getXColor(), getAlpha());
particleUniforms.color.finish = toGlm(getColorFinish(), getAlphaFinish());
particleUniforms.color.spread = toGlm(getColorSpread(), getAlphaSpread());
particleUniforms.lifespan = getLifespan();
// Build particle primitives
auto particlePrimitives = std::make_shared<ParticlePrimitives>();
particlePrimitives->reserve(_particles.size()); // Reserve space
for (auto& particle : _particles) {
particlePrimitives->emplace_back(particle.position, glm::vec2(particle.lifetime, particle.seed));
}
auto bounds = getAABox();
auto position = getPosition();
auto rotation = getRotation();
Transform transform;
transform.setTranslation(position);
transform.setRotation(rotation);
render::PendingChanges pendingChanges;
pendingChanges.updateItem<ParticlePayload>(_renderItemId, [this](ParticlePayload& payload) {
// update vertex buffer
auto vertexBuffer = payload.getVertexBuffer();
size_t numBytes = sizeof(Vertex) * _vertices.size();
pendingChanges.updateItem<ParticlePayloadData>(_renderItemId, [=](ParticlePayloadData& payload) {
payload.setVisibleFlag(true);
// Update particle uniforms
memcpy(&payload.editParticleUniforms(), &particleUniforms, sizeof(ParticleUniforms));
// Update particle buffer
auto particleBuffer = payload.getParticleBuffer();
size_t numBytes = sizeof(ParticlePrimitive) * particlePrimitives->size();
particleBuffer->resize(numBytes);
if (numBytes == 0) {
vertexBuffer->resize(0);
auto indexBuffer = payload.getIndexBuffer();
indexBuffer->resize(0);
return;
}
memcpy(particleBuffer->editData(), particlePrimitives->data(), numBytes);
vertexBuffer->resize(numBytes);
gpu::Byte* data = vertexBuffer->editData();
memcpy(data, &(_vertices[0]), numBytes);
// Update transform and bounds
payload.setModelTransform(transform);
payload.setBound(bounds);
// FIXME, don't update index buffer if num particles has not changed.
// update index buffer
auto indexBuffer = payload.getIndexBuffer();
const size_t NUM_VERTS_PER_PARTICLE = 4;
const size_t NUM_INDICES_PER_PARTICLE = 6;
auto numQuads = (_vertices.size() / NUM_VERTS_PER_PARTICLE);
numBytes = sizeof(uint16_t) * numQuads * NUM_INDICES_PER_PARTICLE;
indexBuffer->resize(numBytes);
data = indexBuffer->editData();
auto indexPtr = reinterpret_cast<uint16_t*>(data);
for (size_t i = 0; i < numQuads; ++i) {
indexPtr[i * NUM_INDICES_PER_PARTICLE + 0] = i * NUM_VERTS_PER_PARTICLE + 0;
indexPtr[i * NUM_INDICES_PER_PARTICLE + 1] = i * NUM_VERTS_PER_PARTICLE + 1;
indexPtr[i * NUM_INDICES_PER_PARTICLE + 2] = i * NUM_VERTS_PER_PARTICLE + 3;
indexPtr[i * NUM_INDICES_PER_PARTICLE + 3] = i * NUM_VERTS_PER_PARTICLE + 1;
indexPtr[i * NUM_INDICES_PER_PARTICLE + 4] = i * NUM_VERTS_PER_PARTICLE + 2;
indexPtr[i * NUM_INDICES_PER_PARTICLE + 5] = i * NUM_VERTS_PER_PARTICLE + 3;
}
// update transform
glm::quat rot = getRotation();
glm::vec3 pos = getPosition();
Transform t;
t.setRotation(rot);
payload.setModelTransform(t);
// transform _particleMinBound and _particleMaxBound corners into world coords
glm::vec3 d = _particleMaxBound - _particleMinBound;
const size_t NUM_BOX_CORNERS = 8;
glm::vec3 corners[NUM_BOX_CORNERS] = {
pos + rot * (_particleMinBound + glm::vec3(0.0f, 0.0f, 0.0f)),
pos + rot * (_particleMinBound + glm::vec3(d.x, 0.0f, 0.0f)),
pos + rot * (_particleMinBound + glm::vec3(0.0f, d.y, 0.0f)),
pos + rot * (_particleMinBound + glm::vec3(d.x, d.y, 0.0f)),
pos + rot * (_particleMinBound + glm::vec3(0.0f, 0.0f, d.z)),
pos + rot * (_particleMinBound + glm::vec3(d.x, 0.0f, d.z)),
pos + rot * (_particleMinBound + glm::vec3(0.0f, d.y, d.z)),
pos + rot * (_particleMinBound + glm::vec3(d.x, d.y, d.z))
};
glm::vec3 min(FLT_MAX, FLT_MAX, FLT_MAX);
glm::vec3 max = -min;
for (size_t i = 0; i < NUM_BOX_CORNERS; i++) {
min.x = std::min(min.x, corners[i].x);
min.y = std::min(min.y, corners[i].y);
min.z = std::min(min.z, corners[i].z);
max.x = std::max(max.x, corners[i].x);
max.y = std::max(max.y, corners[i].y);
max.z = std::max(max.z, corners[i].z);
}
AABox bound(min, max - min);
payload.setBound(bound);
bool textured = _texture && _texture->isLoaded();
if (textured) {
if (_texture && _texture->isLoaded()) {
payload.setTexture(_texture->getGPUTexture());
payload.setPipeline(_texturedPipeline);
} else {
@ -321,46 +276,29 @@ void RenderableParticleEffectEntityItem::updateRenderItem() {
}
void RenderableParticleEffectEntityItem::createPipelines() {
bool writeToDepthBuffer = false;
gpu::State::BlendArg destinationColorBlendArg;
if (_additiveBlending) {
destinationColorBlendArg = gpu::State::ONE;
}
else {
destinationColorBlendArg = gpu::State::INV_SRC_ALPHA;
writeToDepthBuffer = true;
}
if (!_untexturedPipeline) {
auto state = std::make_shared<gpu::State>();
state->setCullMode(gpu::State::CULL_BACK);
state->setDepthTest(true, writeToDepthBuffer, gpu::LESS_EQUAL);
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD,
destinationColorBlendArg, gpu::State::FACTOR_ALPHA,
gpu::State::BLEND_OP_ADD, gpu::State::ONE);
state->setDepthTest(true, false, gpu::LESS_EQUAL);
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
auto vertShader = gpu::Shader::createVertex(std::string(untextured_particle_vert));
auto fragShader = gpu::Shader::createPixel(std::string(untextured_particle_frag));
auto program = gpu::Shader::createProgram(vertShader, fragShader);
_untexturedPipeline = gpu::Pipeline::create(program, state);
}
if (!_texturedPipeline) {
auto state = std::make_shared<gpu::State>();
state->setCullMode(gpu::State::CULL_BACK);
state->setDepthTest(true, false, gpu::LESS_EQUAL);
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
bool writeToDepthBuffer = !_additiveBlending;
state->setDepthTest(true, writeToDepthBuffer, gpu::LESS_EQUAL);
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD,
destinationColorBlendArg, gpu::State::FACTOR_ALPHA,
gpu::State::BLEND_OP_ADD, gpu::State::ONE);
auto vertShader = gpu::Shader::createVertex(std::string(textured_particle_vert));
gpu::ShaderPointer fragShader;
if (_additiveBlending) {
fragShader = gpu::Shader::createPixel(std::string(textured_particle_frag));
}
else {
//If we are sorting and have no additive blending, we want to discard pixels with low alpha to avoid inter-particle entity artifacts
fragShader = gpu::Shader::createPixel(std::string(textured_particle_alpha_discard_frag));
}
auto fragShader = gpu::Shader::createPixel(std::string(textured_particle_frag));
auto program = gpu::Shader::createProgram(vertShader, fragShader);
_texturedPipeline = gpu::Pipeline::create(program, state);
}

View file

@ -16,7 +16,7 @@
#include "RenderableEntityItem.h"
class RenderableParticleEffectEntityItem : public ParticleEffectEntityItem {
friend class ParticlePayload;
friend class ParticlePayloadData;
public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
RenderableParticleEffectEntityItem(const EntityItemID& entityItemID);
@ -29,23 +29,14 @@ public:
virtual void removeFromScene(EntityItemPointer self, render::ScenePointer scene, render::PendingChanges& pendingChanges) override;
protected:
render::ItemID _renderItemId;
struct Vertex {
Vertex(glm::vec3 xyzIn, glm::vec2 uvIn, uint32_t rgbaIn) : xyz(xyzIn), uv(uvIn), rgba(rgbaIn) {}
glm::vec3 xyz;
glm::vec2 uv;
uint32_t rgba;
};
void createPipelines();
std::vector<Vertex> _vertices;
render::ScenePointer _scene;
render::ItemID _renderItemId;
NetworkTexturePointer _texture;
gpu::PipelinePointer _untexturedPipeline;
gpu::PipelinePointer _texturedPipeline;
render::ScenePointer _scene;
NetworkTexturePointer _texture;
};

View file

@ -11,12 +11,11 @@
uniform sampler2D colorMap;
in vec4 _color;
in vec2 _texCoord0;
in vec4 varColor;
in vec2 varTexcoord;
out vec4 outFragColor;
void main(void) {
vec4 color = texture(colorMap, _texCoord0);
outFragColor = color * _color;
outFragColor = texture(colorMap, varTexcoord.xy) * varColor;
}

View file

@ -10,21 +10,84 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include gpu/Inputs.slh@>
<@include gpu/Transform.slh@>
<$declareStandardTransform()$>
out vec4 _color;
out vec2 _texCoord0;
struct Radii {
float start;
float middle;
float finish;
float spread;
};
struct Colors {
vec4 start;
vec4 middle;
vec4 finish;
vec4 spread;
};
struct ParticleUniforms {
Radii radius;
Colors color;
float lifespan;
};
uniform particleBuffer {
ParticleUniforms particle;
};
in vec3 inPosition;
in vec2 inColor; // This is actual Lifetime + Seed
out vec4 varColor;
out vec2 varTexcoord;
const int NUM_VERTICES_PER_PARTICLE = 4;
const vec4 UNIT_QUAD[NUM_VERTICES_PER_PARTICLE] = vec4[NUM_VERTICES_PER_PARTICLE](
vec4(-1.0, -1.0, 0.0, 0.0),
vec4(1.0, -1.0, 0.0, 0.0),
vec4(-1.0, 1.0, 0.0, 0.0),
vec4(1.0, 1.0, 0.0, 0.0)
);
float bezierInterpolate(float y1, float y2, float y3, float u) {
// https://en.wikipedia.org/wiki/Bezier_curve
return (1.0 - u) * (1.0 - u) * y1 + 2.0 * (1.0 - u) * u * y2 + u * u * y3;
}
vec4 interpolate3Vec4(vec4 y1, vec4 y2, vec4 y3, float u) {
return vec4(bezierInterpolate(y1.x, y2.x, y3.x, u),
bezierInterpolate(y1.y, y2.y, y3.y, u),
bezierInterpolate(y1.z, y2.z, y3.z, u),
bezierInterpolate(y1.w, y2.w, y3.w, u));
}
void main(void) {
// pass along the color & uvs to fragment shader
_color = inColor;
_texCoord0 = inTexCoord0.xy;
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();
<$transformModelToClipPos(cam, obj, inPosition, gl_Position)$>
// Which icon are we dealing with ?
int particleID = gl_VertexID / NUM_VERTICES_PER_PARTICLE;
// Which quad vertex pos?
int twoTriID = gl_VertexID - particleID * NUM_VERTICES_PER_PARTICLE;
// Particle properties
float age = inColor.x / particle.lifespan;
float seed = inColor.y;
// Pass the texcoord and the z texcoord is representing the texture icon
varTexcoord = vec2((UNIT_QUAD[twoTriID].xy + 1.0) * 0.5);
varColor = interpolate3Vec4(particle.color.start, particle.color.middle, particle.color.finish, age);
// anchor point in eye space
float radius = bezierInterpolate(particle.radius.start, particle.radius.middle, particle.radius.finish , age);
vec4 quadPos = radius * UNIT_QUAD[twoTriID];
vec4 anchorPoint;
<$transformModelToEyePos(cam, obj, inPosition, anchorPoint)$>
vec4 eyePos = anchorPoint + quadPos;
<$transformEyeToClipPos(cam, eyePos, gl_Position)$>
}

View file

@ -1,25 +0,0 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
// fragment shader
//
// Copyright 2015 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
//
uniform sampler2D colorMap;
in vec4 _color;
in vec2 _texCoord0;
out vec4 outFragColor;
void main(void) {
vec4 color = texture(colorMap, _texCoord0);
if (color.a < 0.1) {
discard;
}
outFragColor = color * _color;
}

View file

@ -36,7 +36,7 @@ int EntityItem::_maxActionsDataSize = 800;
quint64 EntityItem::_rememberDeletedActionTime = 20 * USECS_PER_SECOND;
EntityItem::EntityItem(const EntityItemID& entityItemID) :
SpatiallyNestable(NestableTypes::Entity, entityItemID),
SpatiallyNestable(NestableType::Entity, entityItemID),
_type(EntityTypes::Unknown),
_lastSimulated(0),
_lastUpdated(0),
@ -1294,7 +1294,7 @@ void EntityItem::updatePosition(const glm::vec3& value) {
setLocalPosition(value);
_dirtyFlags |= Simulation::DIRTY_POSITION;
forEachDescendant([&](SpatiallyNestablePointer object) {
if (object->getNestableType() == NestableTypes::Entity) {
if (object->getNestableType() == NestableType::Entity) {
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(object);
entity->_dirtyFlags |= Simulation::DIRTY_POSITION;
}
@ -1317,7 +1317,7 @@ void EntityItem::updateRotation(const glm::quat& rotation) {
setLocalOrientation(rotation);
_dirtyFlags |= Simulation::DIRTY_ROTATION;
forEachDescendant([&](SpatiallyNestablePointer object) {
if (object->getNestableType() == NestableTypes::Entity) {
if (object->getNestableType() == NestableType::Entity) {
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(object);
entity->_dirtyFlags |= Simulation::DIRTY_ROTATION;
entity->_dirtyFlags |= Simulation::DIRTY_POSITION;

View file

@ -382,6 +382,8 @@ public:
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override { return glm::quat(); }
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override { return glm::vec3(0.0f); }
virtual void loader() {} // called indirectly when urls for geometry are updated
protected:
const QByteArray getActionDataInternal() const;

View file

@ -219,7 +219,7 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
// if the entity has children, run UpdateEntityOperator on them. If the children have children, recurse
QQueue<SpatiallyNestablePointer> toProcess;
foreach (SpatiallyNestablePointer child, entity->getChildren()) {
if (child && child->getNestableType() == NestableTypes::Entity) {
if (child && child->getNestableType() == NestableType::Entity) {
toProcess.enqueue(child);
}
}
@ -232,7 +232,7 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
childEntity, newChildBBRelProperties);
recurseTreeWithOperator(&theChildOperator);
foreach (SpatiallyNestablePointer childChild, childEntity->getChildren()) {
if (childChild && childChild->getNestableType() == NestableTypes::Entity) {
if (childChild && childChild->getNestableType() == NestableType::Entity) {
toProcess.enqueue(childChild);
}
}
@ -1266,3 +1266,12 @@ void EntityTree::trackIncomingEntityLastEdited(quint64 lastEditedTime, int bytes
}
}
}
void EntityTree::callLoader(EntityItemID entityID) {
// this is used to bounce from the networking thread to the main thread
EntityItemPointer entity = findEntityByEntityItemID(entityID);
if (entity) {
entity->loader();
}
}

View file

@ -235,6 +235,9 @@ public:
return _deletedEntityItemIDs.contains(id);
}
public slots:
void callLoader(EntityItemID entityID);
signals:
void deletingEntity(const EntityItemID& entityID);
void addingEntity(const EntityItemID& entityID);

View file

@ -74,11 +74,10 @@ public:
_color[GREEN_INDEX] = value.green;
_color[BLUE_INDEX] = value.blue;
}
// model related properties
void setModelURL(const QString& url) { _modelURL = url; _parsedModelURL = QUrl(url); }
virtual void setCompoundShapeURL(const QString& url);
// model related properties
virtual void setModelURL(const QString& url) { _modelURL = url; _parsedModelURL = QUrl(url); }
virtual void setCompoundShapeURL(const QString& url);
// Animation related items...
const AnimationPropertyGroup& getAnimationProperties() const { return _animationProperties; }

View file

@ -40,9 +40,6 @@
#include "EntityScriptingInterface.h"
#include "ParticleEffectEntityItem.h"
const glm::vec3 X_AXIS = glm::vec3(1.0f, 0.0f, 0.0f);
const glm::vec3 Z_AXIS = glm::vec3(0.0f, 0.0f, 1.0f);
const float SCRIPT_MAXIMUM_PI = 3.1416f; // Round up so that reasonable property values work
const xColor ParticleEffectEntityItem::DEFAULT_COLOR = { 255, 255, 255 };
@ -66,8 +63,8 @@ const float ParticleEffectEntityItem::DEFAULT_EMIT_SPEED = 5.0f;
const float ParticleEffectEntityItem::MINIMUM_EMIT_SPEED = 0.0f;
const float ParticleEffectEntityItem::MAXIMUM_EMIT_SPEED = 1000.0f; // Approx mach 3
const float ParticleEffectEntityItem::DEFAULT_SPEED_SPREAD = 1.0f;
const glm::quat ParticleEffectEntityItem::DEFAULT_EMIT_ORIENTATION = glm::angleAxis(-PI_OVER_TWO, X_AXIS); // Vertical
const glm::vec3 ParticleEffectEntityItem::DEFAULT_EMIT_DIMENSIONS = glm::vec3(0.0f, 0.0f, 0.0f); // Emit from point
const glm::quat ParticleEffectEntityItem::DEFAULT_EMIT_ORIENTATION = glm::angleAxis(-PI_OVER_TWO, Vectors::UNIT_X); // Vertical
const glm::vec3 ParticleEffectEntityItem::DEFAULT_EMIT_DIMENSIONS = Vectors::ZERO; // Emit from point
const float ParticleEffectEntityItem::MINIMUM_EMIT_DIMENSION = 0.0f;
const float ParticleEffectEntityItem::MAXIMUM_EMIT_DIMENSION = (float)TREE_SCALE;
const float ParticleEffectEntityItem::DEFAULT_EMIT_RADIUS_START = 1.0f; // Emit from surface (when emitDimensions > 0)
@ -106,36 +103,12 @@ EntityItemPointer ParticleEffectEntityItem::factory(const EntityItemID& entityID
// our non-pure virtual subclass for now...
ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityItemID) :
EntityItem(entityItemID),
_lastSimulated(usecTimestampNow()),
_particleLifetimes(DEFAULT_MAX_PARTICLES, 0.0f),
_particlePositions(DEFAULT_MAX_PARTICLES, glm::vec3(0.0f, 0.0f, 0.0f)),
_particleVelocities(DEFAULT_MAX_PARTICLES, glm::vec3(0.0f, 0.0f, 0.0f)),
_particleAccelerations(DEFAULT_MAX_PARTICLES, glm::vec3(0.0f, 0.0f, 0.0f)),
_particleRadiuses(DEFAULT_MAX_PARTICLES, DEFAULT_PARTICLE_RADIUS),
_radiusStarts(DEFAULT_MAX_PARTICLES, DEFAULT_PARTICLE_RADIUS),
_radiusMiddles(DEFAULT_MAX_PARTICLES, DEFAULT_PARTICLE_RADIUS),
_radiusFinishes(DEFAULT_MAX_PARTICLES, DEFAULT_PARTICLE_RADIUS),
_particleColors(DEFAULT_MAX_PARTICLES, DEFAULT_COLOR),
_colorStarts(DEFAULT_MAX_PARTICLES, DEFAULT_COLOR),
_colorMiddles(DEFAULT_MAX_PARTICLES, DEFAULT_COLOR),
_colorFinishes(DEFAULT_MAX_PARTICLES, DEFAULT_COLOR),
_particleAlphas(DEFAULT_MAX_PARTICLES, DEFAULT_ALPHA),
_alphaStarts(DEFAULT_MAX_PARTICLES, DEFAULT_ALPHA),
_alphaMiddles(DEFAULT_MAX_PARTICLES, DEFAULT_ALPHA),
_alphaFinishes(DEFAULT_MAX_PARTICLES, DEFAULT_ALPHA),
_particleMaxBound(glm::vec3(1.0f, 1.0f, 1.0f)),
_particleMinBound(glm::vec3(-1.0f, -1.0f, -1.0f)) ,
_additiveBlending(DEFAULT_ADDITIVE_BLENDING)
_lastSimulated(usecTimestampNow())
{
_type = EntityTypes::ParticleEffect;
setColor(DEFAULT_COLOR);
}
ParticleEffectEntityItem::~ParticleEffectEntityItem() {
}
void ParticleEffectEntityItem::setAlpha(float alpha) {
if (MINIMUM_ALPHA <= alpha && alpha <= MAXIMUM_ALPHA) {
_alpha = alpha;
@ -310,8 +283,8 @@ void ParticleEffectEntityItem::setRadiusSpread(float radiusSpread) {
void ParticleEffectEntityItem::computeAndUpdateDimensions() {
const float time = _lifespan * 1.1f; // add 10% extra time to account for incremental timer accumulation error
glm::vec3 velocity = _emitSpeed * (_emitOrientation * Z_AXIS);
glm::vec3 velocitySpread = _speedSpread * (_emitOrientation * Z_AXIS);
glm::vec3 velocity = _emitSpeed * (_emitOrientation * Vectors::UNIT_Z);
glm::vec3 velocitySpread = _speedSpread * (_emitOrientation * Vectors::UNIT_Z);
glm::vec3 maxVelocity = glm::abs(velocity) + velocitySpread;
glm::vec3 maxAccleration = glm::abs(_acceleration) + _accelerationSpread;
@ -578,7 +551,7 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData,
bool ParticleEffectEntityItem::isEmittingParticles() const {
// keep emitting if there are particles still alive.
return (getIsEmitting() || getLivingParticleCount() > 0);
return (getIsEmitting() || !_particles.empty());
}
bool ParticleEffectEntityItem::needsToCallUpdate() const {
@ -613,62 +586,25 @@ void ParticleEffectEntityItem::updateShapeType(ShapeType type) {
}
}
void ParticleEffectEntityItem::updateRadius(quint32 index, float age) {
_particleRadiuses[index] = Interpolate::interpolate3Points(_radiusStarts[index], _radiusMiddles[index],
_radiusFinishes[index], age);
}
void ParticleEffectEntityItem::updateColor(quint32 index, float age) {
_particleColors[index].red = (int)Interpolate::interpolate3Points(_colorStarts[index].red, _colorMiddles[index].red,
_colorFinishes[index].red, age);
_particleColors[index].green = (int)Interpolate::interpolate3Points(_colorStarts[index].green, _colorMiddles[index].green,
_colorFinishes[index].green, age);
_particleColors[index].blue = (int)Interpolate::interpolate3Points(_colorStarts[index].blue, _colorMiddles[index].blue,
_colorFinishes[index].blue, age);
}
void ParticleEffectEntityItem::updateAlpha(quint32 index, float age) {
_particleAlphas[index] = Interpolate::interpolate3Points(_alphaStarts[index], _alphaMiddles[index],
_alphaFinishes[index], age);
}
void ParticleEffectEntityItem::extendBounds(const glm::vec3& point) {
_particleMinBound.x = glm::min(_particleMinBound.x, point.x);
_particleMinBound.y = glm::min(_particleMinBound.y, point.y);
_particleMinBound.z = glm::min(_particleMinBound.z, point.z);
_particleMaxBound.x = glm::max(_particleMaxBound.x, point.x);
_particleMaxBound.y = glm::max(_particleMaxBound.y, point.y);
_particleMaxBound.z = glm::max(_particleMaxBound.z, point.z);
}
void ParticleEffectEntityItem::integrateParticle(quint32 index, float deltaTime) {
glm::vec3 accel = _particleAccelerations[index];
glm::vec3 atSquared = (0.5f * deltaTime * deltaTime) * accel;
glm::vec3 at = accel * deltaTime;
_particlePositions[index] += _particleVelocities[index] * deltaTime + atSquared;
_particleVelocities[index] += at;
void ParticleEffectEntityItem::integrateParticle(Particle& particle, float deltaTime) {
glm::vec3 atSquared = (0.5f * deltaTime * deltaTime) * particle.acceleration;
glm::vec3 at = particle.acceleration * deltaTime;
particle.position += particle.velocity * deltaTime + atSquared;
particle.velocity += at;
}
void ParticleEffectEntityItem::stepSimulation(float deltaTime) {
_particleMinBound = glm::vec3(-1.0f, -1.0f, -1.0f);
_particleMaxBound = glm::vec3(1.0f, 1.0f, 1.0f);
// update particles between head and tail
for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) {
_particleLifetimes[i] += deltaTime;
for (Particle& particle : _particles) {
particle.lifetime += deltaTime;
// if particle has died.
if (_particleLifetimes[i] >= _lifespan || _lifespan < EPSILON) {
if (particle.lifetime >= _lifespan) {
// move head forward
_particleHeadIndex = (_particleHeadIndex + 1) % _maxParticles;
_particles.pop_front();
} else {
float age = _particleLifetimes[i] / _lifespan; // 0.0 .. 1.0
updateRadius(i, age);
updateColor(i, age);
updateAlpha(i, age);
integrateParticle(i, deltaTime);
extendBounds(_particlePositions[i]);
// Otherwise update it
integrateParticle(particle, deltaTime);
}
}
@ -677,192 +613,104 @@ void ParticleEffectEntityItem::stepSimulation(float deltaTime) {
float timeLeftInFrame = deltaTime;
while (_timeUntilNextEmit < timeLeftInFrame) {
timeLeftInFrame -= _timeUntilNextEmit;
_timeUntilNextEmit = 1.0f / _emitRate;
// emit a new particle at tail index.
quint32 i = _particleTailIndex;
_particleLifetimes[i] = 0.0f;
// Radius
if (_radiusSpread == 0.0f) {
_radiusStarts[i] = getRadiusStart();
_radiusMiddles[i] =_particleRadius;
_radiusFinishes[i] = getRadiusFinish();
} else {
float spreadMultiplier;
if (_particleRadius > 0.0f) {
spreadMultiplier = 1.0f + randFloatInRange(-1.0f, 1.0f) * _radiusSpread / _particleRadius;
} else {
spreadMultiplier = 1.0f;
}
_radiusStarts[i] =
glm::clamp(spreadMultiplier * getRadiusStart(), MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS);
_radiusMiddles[i] =
glm::clamp(spreadMultiplier * _particleRadius, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS);
_radiusFinishes[i] =
glm::clamp(spreadMultiplier * getRadiusFinish(), MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS);
}
updateRadius(i, 0.0f);
// Position, velocity, and acceleration
if (_polarStart == 0.0f && _polarFinish == 0.0f && _emitDimensions.z == 0.0f) {
// Emit along z-axis from position
_particlePositions[i] = getPosition();
_particleVelocities[i] =
(_emitSpeed + randFloatInRange(-1.0f, 1.0f) * _speedSpread) * (_emitOrientation * Z_AXIS);
_particleAccelerations[i] = _emitAcceleration + randFloatInRange(-1.0f, 1.0f) * _accelerationSpread;
} else {
// Emit around point or from ellipsoid
// - Distribute directions evenly around point
// - Distribute points relatively evenly over ellipsoid surface
// - Distribute points relatively evenly within ellipsoid volume
float elevationMinZ = sin(PI_OVER_TWO - _polarFinish);
float elevationMaxZ = sin(PI_OVER_TWO - _polarStart);
float elevation = asin(elevationMinZ + (elevationMaxZ - elevationMinZ) * randFloat());
float azimuth;
if (_azimuthFinish >= _azimuthStart) {
azimuth = _azimuthStart + (_azimuthFinish - _azimuthStart) * randFloat();
} else {
azimuth = _azimuthStart + (TWO_PI + _azimuthFinish - _azimuthStart) * randFloat();
}
glm::vec3 emitDirection;
if (_emitDimensions == glm::vec3()) {
// Point
emitDirection = glm::quat(glm::vec3(PI_OVER_TWO - elevation, 0.0f, azimuth)) * Z_AXIS;
_particlePositions[i] = getPosition();
} else {
// Ellipsoid
float radiusScale = 1.0f;
if (_emitRadiusStart < 1.0f) {
float emitRadiusStart = glm::max(_emitRadiusStart, EPSILON); // Avoid math complications at center
float randRadius =
emitRadiusStart + randFloatInRange(0.0f, MAXIMUM_EMIT_RADIUS_START - emitRadiusStart);
radiusScale = 1.0f - std::pow(1.0f - randRadius, 3.0f);
}
glm::vec3 radiuses = radiusScale * 0.5f * _emitDimensions;
float x = radiuses.x * glm::cos(elevation) * glm::cos(azimuth);
float y = radiuses.y * glm::cos(elevation) * glm::sin(azimuth);
float z = radiuses.z * glm::sin(elevation);
glm::vec3 emitPosition = glm::vec3(x, y, z);
emitDirection = glm::normalize(glm::vec3(
radiuses.x > 0.0f ? x / (radiuses.x * radiuses.x) : 0.0f,
radiuses.y > 0.0f ? y / (radiuses.y * radiuses.y) : 0.0f,
radiuses.z > 0.0f ? z / (radiuses.z * radiuses.z) : 0.0f
));
_particlePositions[i] = getPosition() + _emitOrientation * emitPosition;
}
_particleVelocities[i] =
(_emitSpeed + randFloatInRange(-1.0f, 1.0f) * _speedSpread) * (_emitOrientation * emitDirection);
_particleAccelerations[i] = _emitAcceleration + randFloatInRange(-1.0f, 1.0f) * _accelerationSpread;
}
integrateParticle(i, timeLeftInFrame);
extendBounds(_particlePositions[i]);
// Color
if (_colorSpread == xColor{ 0, 0, 0 }) {
_colorStarts[i] = getColorStart();
_colorMiddles[i] = getXColor();
_colorFinishes[i] = getColorFinish();
} else {
xColor startColor = getColorStart();
xColor middleColor = getXColor();
xColor finishColor = getColorFinish();
float spread = randFloatInRange(-1.0f, 1.0f);
float spreadMultiplierRed =
middleColor.red > 0 ? 1.0f + spread * (float)_colorSpread.red / (float)middleColor.red : 1.0f;
float spreadMultiplierGreen =
middleColor.green > 0 ? 1.0f + spread * (float)_colorSpread.green / (float)middleColor.green : 1.0f;
float spreadMultiplierBlue =
middleColor.blue > 0 ? 1.0f + spread * (float)_colorSpread.blue / (float)middleColor.blue : 1.0f;
_colorStarts[i].red = (int)glm::clamp(spreadMultiplierRed * (float)startColor.red, 0.0f, 255.0f);
_colorStarts[i].green = (int)glm::clamp(spreadMultiplierGreen * (float)startColor.green, 0.0f, 255.0f);
_colorStarts[i].blue = (int)glm::clamp(spreadMultiplierBlue * (float)startColor.blue, 0.0f, 255.0f);
_colorMiddles[i].red = (int)glm::clamp(spreadMultiplierRed * (float)middleColor.red, 0.0f, 255.0f);
_colorMiddles[i].green = (int)glm::clamp(spreadMultiplierGreen * (float)middleColor.green, 0.0f, 255.0f);
_colorMiddles[i].blue = (int)glm::clamp(spreadMultiplierBlue * (float)middleColor.blue, 0.0f, 255.0f);
_colorFinishes[i].red = (int)glm::clamp(spreadMultiplierRed * (float)finishColor.red, 0.0f, 255.0f);
_colorFinishes[i].green = (int)glm::clamp(spreadMultiplierGreen * (float)finishColor.green, 0.0f, 255.0f);
_colorFinishes[i].blue = (int)glm::clamp(spreadMultiplierBlue * (float)finishColor.blue, 0.0f, 255.0f);
}
updateColor(i, 0.0f);
// Alpha
if (_alphaSpread == 0.0f) {
_alphaStarts[i] = getAlphaStart();
_alphaMiddles[i] = _alpha;
_alphaFinishes[i] = getAlphaFinish();
} else {
float spreadMultiplier = 1.0f + randFloatInRange(-1.0f, 1.0f) * _alphaSpread / _alpha;
_alphaStarts[i] = spreadMultiplier * getAlphaStart();
_alphaMiddles[i] = spreadMultiplier * _alpha;
_alphaFinishes[i] = spreadMultiplier * getAlphaFinish();
}
updateAlpha(i, 0.0f);
_particleTailIndex = (_particleTailIndex + 1) % _maxParticles;
// overflow! move head forward by one.
// because the case of head == tail indicates an empty array, not a full one.
// This can drop an existing older particle, but this is by design, newer particles are a higher priority.
if (_particleTailIndex == _particleHeadIndex) {
_particleHeadIndex = (_particleHeadIndex + 1) % _maxParticles;
if (_particles.size() >= _maxParticles) {
_particles.pop_front();
}
// emit a new particle at tail index.
_particles.push_back(createParticle());
auto particle = _particles.back();
particle.lifetime += timeLeftInFrame;
// Initialize it
integrateParticle(particle, deltaTime);
// Advance in frame
timeLeftInFrame -= _timeUntilNextEmit;
_timeUntilNextEmit = 1.0f / _emitRate;
}
_timeUntilNextEmit -= timeLeftInFrame;
}
}
ParticleEffectEntityItem::Particle ParticleEffectEntityItem::createParticle() {
Particle particle;
particle.seed = randFloatInRange(0.0f, 1.0f);
// Position, velocity, and acceleration
if (_polarStart == 0.0f && _polarFinish == 0.0f && _emitDimensions.z == 0.0f) {
// Emit along z-axis from position
particle.velocity = (_emitSpeed + randFloatInRange(-1.0f, 1.0f) * _speedSpread) * (_emitOrientation * Vectors::UNIT_Z);
particle.acceleration = _emitAcceleration + randFloatInRange(-1.0f, 1.0f) * _accelerationSpread;
} else {
// Emit around point or from ellipsoid
// - Distribute directions evenly around point
// - Distribute points relatively evenly over ellipsoid surface
// - Distribute points relatively evenly within ellipsoid volume
float elevationMinZ = sin(PI_OVER_TWO - _polarFinish);
float elevationMaxZ = sin(PI_OVER_TWO - _polarStart);
float elevation = asin(elevationMinZ + (elevationMaxZ - elevationMinZ) * randFloat());
float azimuth;
if (_azimuthFinish >= _azimuthStart) {
azimuth = _azimuthStart + (_azimuthFinish - _azimuthStart) * randFloat();
} else {
azimuth = _azimuthStart + (TWO_PI + _azimuthFinish - _azimuthStart) * randFloat();
}
glm::vec3 emitDirection;
if (_emitDimensions == Vectors::ZERO) {
// Point
emitDirection = glm::quat(glm::vec3(PI_OVER_TWO - elevation, 0.0f, azimuth)) * Vectors::UNIT_Z;
} else {
// Ellipsoid
float radiusScale = 1.0f;
if (_emitRadiusStart < 1.0f) {
float emitRadiusStart = glm::max(_emitRadiusStart, EPSILON); // Avoid math complications at center
float randRadius =
emitRadiusStart + randFloatInRange(0.0f, MAXIMUM_EMIT_RADIUS_START - emitRadiusStart);
radiusScale = 1.0f - std::pow(1.0f - randRadius, 3.0f);
}
glm::vec3 radii = radiusScale * 0.5f * _emitDimensions;
float x = radii.x * glm::cos(elevation) * glm::cos(azimuth);
float y = radii.y * glm::cos(elevation) * glm::sin(azimuth);
float z = radii.z * glm::sin(elevation);
glm::vec3 emitPosition = glm::vec3(x, y, z);
emitDirection = glm::normalize(glm::vec3(
radii.x > 0.0f ? x / (radii.x * radii.x) : 0.0f,
radii.y > 0.0f ? y / (radii.y * radii.y) : 0.0f,
radii.z > 0.0f ? z / (radii.z * radii.z) : 0.0f
));
particle.position = _emitOrientation * emitPosition;
}
particle.velocity = (_emitSpeed + randFloatInRange(-1.0f, 1.0f) * _speedSpread) * (_emitOrientation * emitDirection);
particle.acceleration = _emitAcceleration + randFloatInRange(-1.0f, 1.0f) * _accelerationSpread;
}
return particle;
}
void ParticleEffectEntityItem::setMaxParticles(quint32 maxParticles) {
if (_maxParticles != maxParticles && MINIMUM_MAX_PARTICLES <= maxParticles && maxParticles <= MAXIMUM_MAX_PARTICLES) {
_maxParticles = maxParticles;
// TODO: try to do something smart here and preserve the state of existing particles.
// resize vectors
_particleLifetimes.resize(_maxParticles);
_particlePositions.resize(_maxParticles);
_particleVelocities.resize(_maxParticles);
_particleRadiuses.resize(_maxParticles);
_radiusStarts.resize(_maxParticles);
_radiusMiddles.resize(_maxParticles);
_radiusFinishes.resize(_maxParticles);
_particleColors.resize(_maxParticles);
_colorStarts.resize(_maxParticles);
_colorMiddles.resize(_maxParticles);
_colorFinishes.resize(_maxParticles);
_particleAlphas.resize(_maxParticles);
_alphaStarts.resize(_maxParticles);
_alphaMiddles.resize(_maxParticles);
_alphaFinishes.resize(_maxParticles);
// Pop all the overflowing oldest particles
while (_particles.size() > _maxParticles) {
_particles.pop_front();
}
// effectively clear all particles and start emitting new ones from scratch.
_particleHeadIndex = 0;
_particleTailIndex = 0;
_timeUntilNextEmit = 0.0f;
}
}
// because particles are in a ring buffer, this isn't trivial
quint32 ParticleEffectEntityItem::getLivingParticleCount() const {
if (_particleTailIndex >= _particleHeadIndex) {
return _particleTailIndex - _particleHeadIndex;
} else {
return (_maxParticles - _particleHeadIndex) + _particleTailIndex;
}
}

View file

@ -11,19 +11,17 @@
#ifndef hifi_ParticleEffectEntityItem_h
#define hifi_ParticleEffectEntityItem_h
#include <AnimationLoop.h>
#include <deque>
#include "EntityItem.h"
class ParticleEffectEntityItem : public EntityItem {
public:
ALLOW_INSTANTIATION // This class can be instantiated
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
ParticleEffectEntityItem(const EntityItemID& entityItemID);
virtual ~ParticleEffectEntityItem();
ALLOW_INSTANTIATION // This class can be instantiated
// methods for getting/setting all properties of this entity
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const;
@ -218,16 +216,27 @@ public:
virtual bool supportsDetailedRayIntersection() const { return false; }
protected:
struct Particle;
using Particles = std::deque<Particle>;
bool isAnimatingSomething() const;
Particle createParticle();
void stepSimulation(float deltaTime);
void updateRadius(quint32 index, float age);
void updateColor(quint32 index, float age);
void updateAlpha(quint32 index, float age);
void extendBounds(const glm::vec3& point);
void integrateParticle(quint32 index, float deltaTime);
quint32 getLivingParticleCount() const;
// the properties of this entity
void integrateParticle(Particle& particle, float deltaTime);
struct Particle {
float seed { 0.0f };
float lifetime { 0.0f };
glm::vec3 position { Vectors::ZERO };
glm::vec3 velocity { Vectors::ZERO };
glm::vec3 acceleration { Vectors::ZERO };
};
// Particles container
Particles _particles;
// Particles properties
rgbColor _color;
xColor _colorStart = DEFAULT_COLOR;
xColor _colorFinish = DEFAULT_COLOR;
@ -236,63 +245,42 @@ protected:
float _alphaStart = DEFAULT_ALPHA_START;
float _alphaFinish = DEFAULT_ALPHA_FINISH;
float _alphaSpread = DEFAULT_ALPHA_SPREAD;
quint32 _maxParticles = DEFAULT_MAX_PARTICLES;
float _lifespan = DEFAULT_LIFESPAN;
float _emitRate = DEFAULT_EMIT_RATE;
float _emitSpeed = DEFAULT_EMIT_SPEED;
float _speedSpread = DEFAULT_SPEED_SPREAD;
glm::quat _emitOrientation = DEFAULT_EMIT_ORIENTATION;
glm::vec3 _emitDimensions = DEFAULT_EMIT_DIMENSIONS;
float _emitRadiusStart = DEFAULT_EMIT_RADIUS_START;
float _polarStart = DEFAULT_POLAR_START;
float _polarFinish = DEFAULT_POLAR_FINISH;
float _azimuthStart = DEFAULT_AZIMUTH_START;
float _azimuthFinish = DEFAULT_AZIMUTH_FINISH;
glm::vec3 _emitAcceleration = DEFAULT_EMIT_ACCELERATION;
glm::vec3 _accelerationSpread = DEFAULT_ACCELERATION_SPREAD;
float _particleRadius = DEFAULT_PARTICLE_RADIUS;
float _radiusStart = DEFAULT_RADIUS_START;
float _radiusFinish = DEFAULT_RADIUS_FINISH;
float _radiusSpread = DEFAULT_RADIUS_SPREAD;
float _lifespan = DEFAULT_LIFESPAN;
// Emiter properties
quint32 _maxParticles = DEFAULT_MAX_PARTICLES;
float _emitRate = DEFAULT_EMIT_RATE;
float _emitSpeed = DEFAULT_EMIT_SPEED;
float _speedSpread = DEFAULT_SPEED_SPREAD;
glm::quat _emitOrientation = DEFAULT_EMIT_ORIENTATION;
glm::vec3 _emitDimensions = DEFAULT_EMIT_DIMENSIONS;
float _emitRadiusStart = DEFAULT_EMIT_RADIUS_START;
glm::vec3 _emitAcceleration = DEFAULT_EMIT_ACCELERATION;
glm::vec3 _accelerationSpread = DEFAULT_ACCELERATION_SPREAD;
float _polarStart = DEFAULT_POLAR_START;
float _polarFinish = DEFAULT_POLAR_FINISH;
float _azimuthStart = DEFAULT_AZIMUTH_START;
float _azimuthFinish = DEFAULT_AZIMUTH_FINISH;
quint64 _lastSimulated { 0 };
bool _isEmitting { true };
quint64 _lastSimulated;
bool _isEmitting = true;
QString _textures { DEFAULT_TEXTURES };
bool _texturesChangedFlag { false };
ShapeType _shapeType { SHAPE_TYPE_NONE };
float _timeUntilNextEmit { 0.0f };
QString _textures = DEFAULT_TEXTURES;
bool _texturesChangedFlag = false;
ShapeType _shapeType = SHAPE_TYPE_NONE;
// all the internals of running the particle sim
QVector<float> _particleLifetimes;
QVector<glm::vec3> _particlePositions;
QVector<glm::vec3> _particleVelocities;
QVector<glm::vec3> _particleAccelerations;
QVector<float> _particleRadiuses;
QVector<float> _radiusStarts;
QVector<float> _radiusMiddles;
QVector<float> _radiusFinishes;
QVector<xColor> _particleColors;
QVector<xColor> _colorStarts;
QVector<xColor> _colorMiddles;
QVector<xColor> _colorFinishes;
QVector<float> _particleAlphas;
QVector<float> _alphaStarts;
QVector<float> _alphaMiddles;
QVector<float> _alphaFinishes;
float _timeUntilNextEmit = 0.0f;
// particle arrays are a ring buffer, use these indices
// to keep track of the living particles.
quint32 _particleHeadIndex = 0;
quint32 _particleTailIndex = 0;
// bounding volume
glm::vec3 _particleMaxBound;
glm::vec3 _particleMinBound;
bool _additiveBlending;
bool _additiveBlending { DEFAULT_ADDITIVE_BLENDING };
};
#endif // hifi_ParticleEffectEntityItem_h

View file

@ -57,7 +57,7 @@ public:
static bool getDrawZoneBoundaries() { return _drawZoneBoundaries; }
static void setDrawZoneBoundaries(bool value) { _drawZoneBoundaries = value; }
virtual bool isReadyToComputeShape() { return false; }
virtual bool isReadyToComputeShape() { return true; }
void updateShapeType(ShapeType type) { _shapeType = type; }
virtual ShapeType getShapeType() const;

View file

@ -154,7 +154,7 @@ GLBackend::GLShader* compileShader(const Shader& shader) {
qCWarning(gpulogging) << "GLShader::compileShader - failed to compile the gl shader object:";
qCWarning(gpulogging) << temp;
/*
filestream.open("debugshader.glsl.info.txt");
if (filestream.is_open()) {

View file

@ -77,16 +77,6 @@ bool Stream::Format::setAttribute(Slot slot, Slot channel, Frequency frequency)
return true;
}
BufferStream::BufferStream() :
_buffers(),
_offsets(),
_strides()
{}
BufferStream::~BufferStream() {
}
void BufferStream::addBuffer(const BufferPointer& buffer, Offset offset, Offset stride) {
_buffers.push_back(buffer);
_offsets.push_back(offset);

View file

@ -50,7 +50,7 @@ public:
// Frequency describer
enum Frequency {
PER_VERTEX = 0,
PER_INSTANCE,
PER_INSTANCE = 1,
};
// The attribute description
@ -125,10 +125,7 @@ typedef std::vector< Offset > Offsets;
// A Buffer Stream can be assigned to the Batch to set several stream channels in one call
class BufferStream {
public:
typedef Offsets Strides;
BufferStream();
~BufferStream();
using Strides = Offsets;
void clear() { _buffers.clear(); _offsets.clear(); _strides.clear(); }
void addBuffer(const BufferPointer& buffer, Offset offset, Offset stride);

View file

@ -86,7 +86,6 @@ TransformCamera getTransformCamera() {
}
<@endfunc@>
<@func transformModelToWorldPos(objectTransform, modelPos, worldPos)@>
{ // transformModelToWorldPos
<$worldPos$> = (<$objectTransform$>._model * <$modelPos$>);
@ -136,7 +135,22 @@ TransformCamera getTransformCamera() {
<@func transformClipToEyeDir(cameraTransform, clipPos, eyeDir)@>
{ // transformClipToEyeDir
<$eyeDir$> = vec3(<$cameraTransform$>._projectionInverse * vec4(<$clipPos$>.xyz, 1.0));
<$eyeDir$> = vec3(<$cameraTransform$>._projectionInverse * vec4(<$clipPos$>.xyz, 0.0));
}
<@endfunc@>
<@func $transformModelToEyePos(cameraTransform, objectTransform, modelPos, eyePos)@>
<!// Equivalent to the following but hoppefully a tad more accurate
//return camera._view * object._model * pos; !>
{ // transformModelToEyePos
vec4 _worldpos = (<$objectTransform$>._model * vec4(<$modelPos$>.xyz, 1.0));
<$eyePos$> = (<$cameraTransform$>._view * _worldpos);
}
<@endfunc@>
<@func transformEyeToClipPos(cameraTransform, eyePos, clipPos)@>
{ // transformEyeToClipPos
<$clipPos$> = <$cameraTransform$>._projection * vec4(<$eyePos$>.xyz, 1.0);
}
<@endfunc@>

View file

@ -1,8 +1,7 @@
set(TARGET_NAME input-plugins)
setup_hifi_library()
link_hifi_libraries(shared plugins controllers)
link_hifi_libraries(shared plugins controllers script-engine)
GroupSources("src/input-plugins")
target_sdl2()
target_sixense()

View file

@ -14,14 +14,12 @@
#include "KeyboardMouseDevice.h"
#include "SDL2Manager.h"
#include "SixenseManager.h"
// TODO migrate to a DLL model where plugins are discovered and loaded at runtime by the PluginManager class
InputPluginList getInputPlugins() {
InputPlugin* PLUGIN_POOL[] = {
new KeyboardMouseDevice(),
new SDL2Manager(),
new SixenseManager(),
nullptr
};

View file

@ -18,7 +18,7 @@ Light::Light() :
_transform() {
// only if created from nothing shall we create the Buffer to store the properties
Schema schema;
_schemaBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema));
_schemaBuffer = std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema);
}
Light::Light(const Light& light) :

View file

@ -112,8 +112,6 @@ public:
Vec4 _shadow{0.0f};
Vec4 _control{0.0f, 0.0f, 0.0f, 0.0f};
Schema() {}
};
const UniformBufferView& getSchemaBuffer() const { return _schemaBuffer; }

View file

@ -319,10 +319,10 @@ void Resource::attemptRequest() {
void Resource::finishedLoading(bool success) {
if (success) {
qDebug().noquote() << "Finished loading:" << _url.toDisplayString();
qCDebug(networking).noquote() << "Finished loading:" << _url.toDisplayString();
_loaded = true;
} else {
qDebug().noquote() << "Failed to load:" << _url.toDisplayString();
qCDebug(networking).noquote() << "Failed to load:" << _url.toDisplayString();
_failedToLoad = true;
}
_loadPriorities.clear();
@ -339,13 +339,13 @@ void Resource::makeRequest() {
_request = ResourceManager::createResourceRequest(this, _activeUrl);
if (!_request) {
qDebug().noquote() << "Failed to get request for" << _url.toDisplayString();
qCDebug(networking).noquote() << "Failed to get request for" << _url.toDisplayString();
ResourceCache::requestCompleted(this);
finishedLoading(false);
return;
}
qDebug().noquote() << "Starting request for:" << _url.toDisplayString();
qCDebug(networking).noquote() << "Starting request for:" << _url.toDisplayString();
connect(_request, &ResourceRequest::progress, this, &Resource::handleDownloadProgress);
connect(_request, &ResourceRequest::finished, this, &Resource::handleReplyFinished);
@ -369,7 +369,7 @@ void Resource::handleReplyFinished() {
if (result == ResourceRequest::Success) {
_data = _request->getData();
auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString());
qDebug().noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo);
qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo);
finishedLoading(true);
emit loaded(_data);
@ -377,7 +377,7 @@ void Resource::handleReplyFinished() {
} else {
switch (result) {
case ResourceRequest::Result::Timeout: {
qDebug() << "Timed out loading" << _url << "received" << _bytesReceived << "total" << _bytesTotal;
qCDebug(networking) << "Timed out loading" << _url << "received" << _bytesReceived << "total" << _bytesTotal;
// Fall through to other cases
}
case ResourceRequest::Result::ServerUnavailable: {
@ -386,7 +386,7 @@ void Resource::handleReplyFinished() {
const int BASE_DELAY_MS = 1000;
if (_attempts++ < MAX_ATTEMPTS) {
auto waitTime = BASE_DELAY_MS * (int)pow(2.0, _attempts);
qDebug().nospace() << "Retrying to load the asset in " << waitTime
qCDebug(networking).nospace() << "Retrying to load the asset in " << waitTime
<< "ms, attempt " << _attempts << " of " << MAX_ATTEMPTS;
QTimer::singleShot(waitTime, this, &Resource::attemptRequest);
break;
@ -394,7 +394,7 @@ void Resource::handleReplyFinished() {
// fall through to final failure
}
default: {
qDebug() << "Error loading " << _url;
qCDebug(networking) << "Error loading " << _url;
auto error = (result == ResourceRequest::Timeout) ? QNetworkReply::TimeoutError
: QNetworkReply::UnknownNetworkError;
emit failed(error);

View file

@ -58,6 +58,7 @@ public:
/// By default, all HMDs are stereo
virtual bool isStereo() const { return isHmd(); }
virtual bool isThrottled() const { return false; }
virtual float getTargetFrameRate() { return 0.0f; }
// Rendering support

View file

@ -183,7 +183,7 @@ void ScriptEngine::runInThread() {
QSet<ScriptEngine*> ScriptEngine::_allKnownScriptEngines;
QMutex ScriptEngine::_allScriptsMutex;
bool ScriptEngine::_stoppingAllScripts = false;
std::atomic<bool> ScriptEngine::_stoppingAllScripts { false };
void ScriptEngine::stopAllScripts(QObject* application) {
_allScriptsMutex.lock();
@ -752,7 +752,7 @@ void ScriptEngine::run() {
}
if (_wantSignals) {
emit finished(_fileNameString);
emit finished(_fileNameString, this);
}
_isRunning = false;
@ -775,6 +775,10 @@ void ScriptEngine::stopAllTimers() {
void ScriptEngine::stop() {
if (!_isFinished) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "stop");
return;
}
_isFinished = true;
if (_wantSignals) {
emit runningStateChanged();

View file

@ -153,7 +153,7 @@ signals:
void errorLoadingScript(const QString& scriptFilename);
void update(float deltaTime);
void scriptEnding();
void finished(const QString& fileNameString);
void finished(const QString& fileNameString, ScriptEngine* engine);
void cleanupMenuItem(const QString& menuItemString);
void printedMessage(const QString& message);
void errorMessage(const QString& message);
@ -166,8 +166,8 @@ signals:
protected:
QString _scriptContents;
QString _parentURL;
bool _isFinished { false };
bool _isRunning { false };
std::atomic<bool> _isFinished { false };
std::atomic<bool> _isRunning { false };
int _evaluatesPending { 0 };
bool _isInitialized { false };
QHash<QTimer*, QScriptValue> _timerFunctionMap;
@ -193,7 +193,7 @@ protected:
Quat _quatLibrary;
Vec3 _vec3Library;
ScriptUUID _uuidLibrary;
bool _isUserLoaded { false };
std::atomic<bool> _isUserLoaded { false };
bool _isReloading { false };
ArrayBufferClass* _arrayBufferClass;
@ -206,7 +206,7 @@ protected:
static QSet<ScriptEngine*> _allKnownScriptEngines;
static QMutex _allScriptsMutex;
static bool _stoppingAllScripts;
static std::atomic<bool> _stoppingAllScripts;
};
#endif // hifi_ScriptEngine_h

View file

@ -43,9 +43,48 @@ glm::vec3 AABox::calcCenter() const {
return center;
}
void AABox::scale(float scale) {
_corner = _corner * scale;
_scale = _scale * scale;
void AABox::rotate(const glm::quat& rotation) {
auto minimum = _corner;
auto maximum = _corner + _scale;
glm::vec3 bottomLeftNear(minimum.x, minimum.y, minimum.z);
glm::vec3 bottomRightNear(maximum.x, minimum.y, minimum.z);
glm::vec3 bottomLeftFar(minimum.x, minimum.y, maximum.z);
glm::vec3 bottomRightFar(maximum.x, minimum.y, maximum.z);
glm::vec3 topLeftNear(minimum.x, maximum.y, minimum.z);
glm::vec3 topRightNear(maximum.x, maximum.y, minimum.z);
glm::vec3 topLeftFar(minimum.x, maximum.y, maximum.z);
glm::vec3 topRightFar(maximum.x, maximum.y, maximum.z);
glm::vec3 bottomLeftNearRotated = rotation * bottomLeftNear;
glm::vec3 bottomRightNearRotated = rotation * bottomRightNear;
glm::vec3 bottomLeftFarRotated = rotation * bottomLeftFar;
glm::vec3 bottomRightFarRotated = rotation * bottomRightFar;
glm::vec3 topLeftNearRotated = rotation * topLeftNear;
glm::vec3 topRightNearRotated = rotation * topRightNear;
glm::vec3 topLeftFarRotated = rotation * topLeftFar;
glm::vec3 topRightFarRotated = rotation * topRightFar;
minimum = glm::min(bottomLeftNearRotated,
glm::min(bottomRightNearRotated,
glm::min(bottomLeftFarRotated,
glm::min(bottomRightFarRotated,
glm::min(topLeftNearRotated,
glm::min(topRightNearRotated,
glm::min(topLeftFarRotated,
topRightFarRotated)))))));
maximum = glm::max(bottomLeftNearRotated,
glm::max(bottomRightNearRotated,
glm::max(bottomLeftFarRotated,
glm::max(bottomRightFarRotated,
glm::max(topLeftNearRotated,
glm::max(topRightNearRotated,
glm::max(topLeftFarRotated,
topRightFarRotated)))))));
_corner = minimum;
_scale = maximum - minimum;
}
glm::vec3 AABox::getVertex(BoxVertex vertex) const {

View file

@ -35,12 +35,13 @@ public:
AABox(const glm::vec3& corner, const glm::vec3& dimensions);
AABox();
~AABox() {};
void setBox(const glm::vec3& corner, const glm::vec3& scale);
void setBox(const glm::vec3& corner, float scale);
glm::vec3 getVertexP(const glm::vec3& normal) const;
glm::vec3 getVertexN(const glm::vec3& normal) const;
const glm::vec3& getCorner() const { return _corner; }
const glm::vec3& getScale() const { return _scale; }
const glm::vec3& getDimensions() const { return _scale; }

View file

@ -8,10 +8,11 @@
//
#include "DebugDraw.h"
#include "SharedUtil.h"
DebugDraw& DebugDraw::getInstance() {
static DebugDraw instance;
return instance;
static DebugDraw* instance = globalInstance<DebugDraw>("com.highfidelity.DebugDraw");
return *instance;
}
DebugDraw::DebugDraw() {

View file

@ -360,6 +360,10 @@ QSize fromGlm(const glm::ivec2 & v) {
return QSize(v.x, v.y);
}
vec4 toGlm(const xColor& color, float alpha) {
return vec4((float)color.red / 255.0f, (float)color.green / 255.0f, (float)color.blue / 255.0f, alpha);
}
QRectF glmToRect(const glm::vec2 & pos, const glm::vec2 & size) {
QRectF result(pos.x, pos.y, size.x, size.y);
return result;

View file

@ -156,6 +156,7 @@ vec2 toGlm(const QPointF& pt);
vec3 toGlm(const xColor& color);
vec4 toGlm(const QColor& color);
ivec4 toGlm(const QRect& rect);
vec4 toGlm(const xColor& color, float alpha);
QSize fromGlm(const glm::ivec2 & v);
QMatrix4x4 fromGlm(const glm::mat4 & m);

View file

@ -15,7 +15,7 @@
#include "SpatiallyNestable.h"
SpatiallyNestable::SpatiallyNestable(NestableTypes::NestableType nestableType, QUuid id) :
SpatiallyNestable::SpatiallyNestable(NestableType nestableType, QUuid id) :
_nestableType(nestableType),
_id(id),
_transform() {

View file

@ -25,17 +25,14 @@ using SpatiallyNestableWeakConstPointer = std::weak_ptr<const SpatiallyNestable>
using SpatiallyNestablePointer = std::shared_ptr<SpatiallyNestable>;
using SpatiallyNestableConstPointer = std::shared_ptr<const SpatiallyNestable>;
class NestableTypes {
public:
using NestableType = enum NestableType_t {
Entity,
Avatar
};
enum class NestableType {
Entity,
Avatar
};
class SpatiallyNestable : public std::enable_shared_from_this<SpatiallyNestable> {
public:
SpatiallyNestable(NestableTypes::NestableType nestableType, QUuid id);
SpatiallyNestable(NestableType nestableType, QUuid id);
virtual ~SpatiallyNestable() { }
virtual const QUuid& getID() const { return _id; }
@ -88,17 +85,17 @@ public:
virtual void setLocalScale(const glm::vec3& scale);
QList<SpatiallyNestablePointer> getChildren() const;
NestableTypes::NestableType getNestableType() const { return _nestableType; }
NestableType getNestableType() const { return _nestableType; }
// this object's frame
virtual const Transform getAbsoluteJointTransformInObjectFrame(int jointIndex) const;
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const { assert(false); return glm::quat(); }
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const { assert(false); return glm::vec3(); }
SpatiallyNestablePointer getThisPointer() const;
protected:
NestableTypes::NestableType _nestableType; // EntityItem or an AvatarData
const NestableType _nestableType; // EntityItem or an AvatarData
QUuid _id;
QUuid _parentID; // what is this thing's transform relative to?
quint16 _parentJointIndex { 0 }; // which joint of the parent is this relative to?

View file

@ -0,0 +1,12 @@
#
# Created by Bradley Austin Davis on 2015/11/18
# Copyright 2015 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
#
set(TARGET_NAME hifiSixense)
setup_hifi_plugin(Script Qml Widgets)
link_hifi_libraries(shared controllers plugins input-plugins)
target_sixense()

View file

@ -27,9 +27,11 @@
#include <PerfStat.h>
#include <plugins/PluginContainer.h>
#include <SettingHandle.h>
#include <UserActivityLogger.h>
#include "InputPluginsLogging.h"
#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(inputplugins)
Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins")
static const unsigned int BUTTON_0 = 1U << 0; // the skinny button between 1 and 2
static const unsigned int BUTTON_1 = 1U << 5;

View file

@ -17,7 +17,7 @@
#include <controllers/InputDevice.h>
#include <controllers/StandardControls.h>
#include "InputPlugin.h"
#include <plugins/InputPlugin.h>
struct _sixenseControllerData;
using SixenseControllerData = _sixenseControllerData;

View file

@ -0,0 +1,45 @@
//
// Created by Bradley Austin Davis on 2015/10/25
// Copyright 2015 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 <mutex>
#include <QtCore/QObject>
#include <QtCore/QtPlugin>
#include <QtCore/QStringList>
#include <plugins/RuntimePlugin.h>
#include <plugins/InputPlugin.h>
#include "SixenseManager.h"
class SixenseProvider : public QObject, public InputProvider
{
Q_OBJECT
Q_PLUGIN_METADATA(IID InputProvider_iid FILE "plugin.json")
Q_INTERFACES(InputProvider)
public:
SixenseProvider(QObject* parent = nullptr) : QObject(parent) {}
virtual ~SixenseProvider() {}
virtual InputPluginList getInputPlugins() override {
static std::once_flag once;
std::call_once(once, [&] {
InputPluginPointer plugin(new SixenseManager());
if (plugin->isSupported()) {
_inputPlugins.push_back(plugin);
}
});
return _inputPlugins;
}
private:
InputPluginList _inputPlugins;
};
#include "SixenseProvider.moc"

View file

@ -17,8 +17,7 @@
#include <QtCore/QCoreApplication>
#include <QtCore/QLibrary>
#include "InputPluginsLogging.h"
#include <QtCore/QDebug>
#ifndef SIXENSE_LIB_FILENAME
#define SIXENSE_LIB_FILENAME QCoreApplication::applicationDirPath() + "/../Frameworks/libsixense_x64"

View file

@ -0,0 +1 @@
{}

View file

@ -12,12 +12,16 @@
struct SwapFramebufferWrapper;
using SwapFboPtr = QSharedPointer<SwapFramebufferWrapper>;
const float TARGET_RATE_Oculus = 75.0f;
class OculusDisplayPlugin : public OculusBaseDisplayPlugin {
public:
virtual void activate() override;
virtual const QString & getName() const override;
virtual void setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) override final;
virtual float getTargetFrameRate() override { return TARGET_RATE_Oculus; }
protected:
virtual void internalPresent() override;
virtual void customizeContext() override;

View file

@ -13,6 +13,8 @@
#include <OVR_CAPI.h>
const float TARGET_RATE_OculusLegacy = 75.0f;
class OculusLegacyDisplayPlugin : public WindowOpenGLDisplayPlugin {
public:
OculusLegacyDisplayPlugin();
@ -25,6 +27,8 @@ public:
virtual bool eventFilter(QObject* receiver, QEvent* event) override;
virtual int getHmdScreen() const override;
virtual float getTargetFrameRate() override { return TARGET_RATE_OculusLegacy; }
// Stereo specific methods
virtual bool isHmd() const override { return true; }
virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override;

View file

@ -13,12 +13,16 @@
#include <display-plugins/WindowOpenGLDisplayPlugin.h>
const float TARGET_RATE_OpenVr = 90.0f; // FIXME: get from sdk tracked device property? This number is vive-only.
class OpenVrDisplayPlugin : public WindowOpenGLDisplayPlugin {
public:
virtual bool isSupported() const override;
virtual const QString & getName() const override;
virtual bool isHmd() const override { return true; }
virtual float getTargetFrameRate() override { return TARGET_RATE_OpenVr; }
virtual void activate() override;
virtual void deactivate() override;