mirror of
https://github.com/overte-org/overte.git
synced 2025-04-21 04:03:59 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into near-grab-via-parenting
This commit is contained in:
commit
4982ef5943
51 changed files with 1036 additions and 825 deletions
|
@ -102,8 +102,10 @@ void Agent::handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNo
|
|||
packetType = message->getType();
|
||||
} // fall through to piggyback message
|
||||
|
||||
if (packetType == PacketType::EntityData || packetType == PacketType::EntityErase) {
|
||||
if (packetType == PacketType::EntityData) {
|
||||
_entityViewer.processDatagram(*message, senderNode);
|
||||
} else if (packetType == PacketType::EntityErase) {
|
||||
_entityViewer.processEraseMessage(*message, senderNode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
8
cmake/externals/sdl2/CMakeLists.txt
vendored
8
cmake/externals/sdl2/CMakeLists.txt
vendored
|
@ -63,7 +63,11 @@ set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
|||
|
||||
|
||||
if (APPLE)
|
||||
|
||||
# NOOP
|
||||
|
||||
elseif (WIN32)
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${SOURCE_DIR}/include CACHE PATH "Location of SDL2 include directory")
|
||||
|
||||
|
@ -75,8 +79,12 @@ elseif (WIN32)
|
|||
set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/lib/x86 CACHE PATH "Location of SDL2 DLL")
|
||||
endif()
|
||||
|
||||
add_paths_to_fixup_libs(${${EXTERNAL_NAME_UPPER}_DLL_PATH})
|
||||
|
||||
else ()
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include/SDL2 CACHE PATH "Location of SDL2 include directory")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${INSTALL_DIR}/lib/libSDL2.so CACHE FILEPATH "Path to SDL2 library")
|
||||
|
||||
endif ()
|
||||
|
|
12
cmake/externals/sixense/CMakeLists.txt
vendored
12
cmake/externals/sixense/CMakeLists.txt
vendored
|
@ -8,14 +8,14 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
|||
#set(SIXENSE_URL_MD5 "10cc8dc470d2ac1244a88cf04bc549cc")
|
||||
#set(SIXENSE_NEW_LAYOUT 0)
|
||||
|
||||
#set(SIXENSE_URL "http://public.s3.amazonaws.com/dependencies/SixenseSDK_071615.zip")
|
||||
#set(SIXENSE_URL_MD5 "752a3901f334124e9cffc2ba4136ef7d")
|
||||
#set(SIXENSE_NEW_LAYOUT 1)
|
||||
|
||||
set(SIXENSE_URL "http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_102215.zip")
|
||||
set(SIXENSE_URL_MD5 "93c3a6795cce777a0f472b09532935f1")
|
||||
set(SIXENSE_URL "http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_071615.zip")
|
||||
set(SIXENSE_URL_MD5 "752a3901f334124e9cffc2ba4136ef7d")
|
||||
set(SIXENSE_NEW_LAYOUT 1)
|
||||
|
||||
#set(SIXENSE_URL "http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_102215.zip")
|
||||
#set(SIXENSE_URL_MD5 "93c3a6795cce777a0f472b09532935f1")
|
||||
#set(SIXENSE_NEW_LAYOUT 1)
|
||||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL ${SIXENSE_URL}
|
||||
|
|
|
@ -128,7 +128,6 @@ function setUp() {
|
|||
blue: 255
|
||||
},
|
||||
lifespan: 5.0,
|
||||
visible: false,
|
||||
locked: false,
|
||||
isEmitting: true,
|
||||
lifetime: 3600 // 1 hour; just in case
|
||||
|
|
|
@ -68,7 +68,6 @@
|
|||
#include <HFBackEvent.h>
|
||||
#include <InfoView.h>
|
||||
#include <input-plugins/InputPlugin.h>
|
||||
#include <input-plugins/Joystick.h> // this should probably be removed
|
||||
#include <controllers/UserInputMapper.h>
|
||||
#include <controllers/StateController.h>
|
||||
#include <LogHandler.h>
|
||||
|
@ -352,6 +351,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 +371,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 +863,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 +874,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 +920,7 @@ void Application::emptyLocalCache() {
|
|||
}
|
||||
|
||||
Application::~Application() {
|
||||
EntityTreePointer tree = _entities.getTree();
|
||||
EntityTreePointer tree = getEntities()->getTree();
|
||||
tree->setSimulation(NULL);
|
||||
|
||||
_octreeProcessor.terminate();
|
||||
|
@ -1976,7 +1975,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 +2001,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 +2046,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 +2369,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 +2413,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 +2481,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 +2520,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 +2534,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 +2887,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 +2917,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,9 +2937,9 @@ 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();
|
||||
|
@ -3764,7 +3763,7 @@ void Application::clearDomainOctreeDetails() {
|
|||
});
|
||||
|
||||
// reset the model renderer
|
||||
_entities.clear();
|
||||
getEntities()->clear();
|
||||
}
|
||||
|
||||
void Application::domainChanged(const QString& domainHostname) {
|
||||
|
@ -3886,7 +3885,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;
|
||||
}
|
||||
|
@ -3899,6 +3898,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;
|
||||
}
|
||||
}
|
||||
|
@ -3950,11 +3952,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;
|
||||
|
@ -4004,20 +4016,13 @@ void Application::saveScripts() {
|
|||
settings.endArray();
|
||||
}
|
||||
|
||||
QScriptValue joystickToScriptValue(QScriptEngine *engine, Joystick* const &in) {
|
||||
return engine->newQObject(in);
|
||||
}
|
||||
|
||||
void joystickFromScriptValue(const QScriptValue &object, Joystick* &out) {
|
||||
out = qobject_cast<Joystick*>(object.toQObject());
|
||||
}
|
||||
|
||||
void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) {
|
||||
// setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so
|
||||
// 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);
|
||||
|
@ -4075,8 +4080,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
|
||||
scriptEngine->registerGlobalObject("AvatarManager", DependencyManager::get<AvatarManager>().data());
|
||||
|
||||
qScriptRegisterMetaType(scriptEngine, joystickToScriptValue, joystickFromScriptValue);
|
||||
|
||||
scriptEngine->registerGlobalObject("UndoStack", &_undoStackScriptingInterface);
|
||||
|
||||
scriptEngine->registerGlobalObject("LODManager", DependencyManager::get<LODManager>().data());
|
||||
|
|
|
@ -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; }
|
||||
|
@ -444,7 +444,6 @@ private:
|
|||
PhysicalEntitySimulation _entitySimulation;
|
||||
PhysicsEnginePointer _physicsEngine;
|
||||
|
||||
EntityTreeRenderer _entities;
|
||||
EntityTreeRenderer _entityClipboardRenderer;
|
||||
EntityTreePointer _entityClipboard;
|
||||
|
||||
|
|
|
@ -95,7 +95,6 @@ int Basic2DWindowOpenGLDisplayPlugin::getDesiredInterval() const {
|
|||
result = MSECS_PER_SECOND / THROTTLED_FRAMERATE;
|
||||
}
|
||||
|
||||
qDebug() << "New interval " << result;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -138,7 +137,6 @@ void Basic2DWindowOpenGLDisplayPlugin::updateFramerate() {
|
|||
}
|
||||
|
||||
int newInterval = getDesiredInterval();
|
||||
qDebug() << newInterval;
|
||||
_timer.start(newInterval);
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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());
|
||||
|
@ -584,11 +624,3 @@ glm::vec3 RenderableModelEntityItem::getAbsoluteJointTranslationInObjectFrame(in
|
|||
}
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::locationChanged() {
|
||||
EntityItem::locationChanged();
|
||||
if (_model && _model->isActive()) {
|
||||
_model->setRotation(getRotation());
|
||||
_model->setTranslation(getPosition());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,7 +72,7 @@ public:
|
|||
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override;
|
||||
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override;
|
||||
|
||||
void locationChanged();
|
||||
virtual void loader() override;
|
||||
|
||||
private:
|
||||
void remapTextures();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)$>
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -50,7 +50,7 @@ public:
|
|||
// Frequency describer
|
||||
enum Frequency {
|
||||
PER_VERTEX = 0,
|
||||
PER_INSTANCE,
|
||||
PER_INSTANCE = 1,
|
||||
};
|
||||
|
||||
// The attribute description
|
||||
|
@ -106,6 +106,7 @@ public:
|
|||
bool setAttribute(Slot slot, Frequency frequency = PER_VERTEX);
|
||||
bool setAttribute(Slot slot, Slot channel, Frequency frequency = PER_VERTEX);
|
||||
|
||||
bool hasAttribute(Slot slot) const { return (_attributes.find(slot) != _attributes.end()); }
|
||||
|
||||
protected:
|
||||
AttributeMap _attributes;
|
||||
|
@ -124,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);
|
||||
|
|
|
@ -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@>
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
set(TARGET_NAME input-plugins)
|
||||
setup_hifi_library()
|
||||
link_hifi_libraries(shared plugins controllers script-engine)
|
||||
link_hifi_libraries(shared plugins controllers)
|
||||
|
||||
GroupSources("src/input-plugins")
|
||||
|
||||
target_sdl2()
|
||||
|
|
|
@ -13,13 +13,11 @@
|
|||
#include <plugins/PluginManager.h>
|
||||
|
||||
#include "KeyboardMouseDevice.h"
|
||||
#include "SDL2Manager.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(),
|
||||
nullptr
|
||||
};
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ void Mesh::setPartBuffer(const BufferView& buffer) {
|
|||
_partBuffer = buffer;
|
||||
}
|
||||
|
||||
const Box Mesh::evalPartBound(int partNum) const {
|
||||
Box Mesh::evalPartBound(int partNum) const {
|
||||
Box box;
|
||||
if (partNum < _partBuffer.getNum<Part>()) {
|
||||
const Part& part = _partBuffer.get<Part>(partNum);
|
||||
|
@ -111,7 +111,7 @@ const Box Mesh::evalPartBound(int partNum) const {
|
|||
return box;
|
||||
}
|
||||
|
||||
const Box Mesh::evalPartBounds(int partStart, int partEnd, Boxes& bounds) const {
|
||||
Box Mesh::evalPartBounds(int partStart, int partEnd, Boxes& bounds) const {
|
||||
Box totalBound;
|
||||
auto part = _partBuffer.cbegin<Part>() + partStart;
|
||||
auto partItEnd = _partBuffer.cbegin<Part>() + partEnd;
|
||||
|
|
|
@ -107,10 +107,10 @@ public:
|
|||
uint getNumParts() const { return _partBuffer.getNumElements(); }
|
||||
|
||||
// evaluate the bounding box of A part
|
||||
const Box evalPartBound(int partNum) const;
|
||||
Box evalPartBound(int partNum) const;
|
||||
// evaluate the bounding boxes of the parts in the range [start, end[ and fill the bounds parameter
|
||||
// the returned box is the bounding box of ALL the evaluated part bounds.
|
||||
const Box evalPartBounds(int partStart, int partEnd, Boxes& bounds) const;
|
||||
Box evalPartBounds(int partStart, int partEnd, Boxes& bounds) const;
|
||||
|
||||
static gpu::Primitive topologyToPrimitive(Topology topo) { return static_cast<gpu::Primitive>(topo); }
|
||||
|
||||
|
|
|
@ -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) :
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace render {
|
|||
// Return opaque for lack of a better idea
|
||||
return ItemKey::Builder::opaqueShape();
|
||||
}
|
||||
|
||||
|
||||
template <> const Item::Bound payloadGetBound(const MeshPartPayload::Pointer& payload) {
|
||||
if (payload) {
|
||||
return payload->getBound();
|
||||
|
@ -39,55 +39,40 @@ namespace render {
|
|||
|
||||
using namespace render;
|
||||
|
||||
MeshPartPayload::MeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex,
|
||||
glm::vec3 position, glm::quat orientation) :
|
||||
model(model),
|
||||
meshIndex(meshIndex),
|
||||
partIndex(partIndex),
|
||||
_shapeID(shapeIndex),
|
||||
_modelPosition(position),
|
||||
_modelOrientation(orientation) {
|
||||
initCache();
|
||||
MeshPartPayload::MeshPartPayload(model::MeshPointer mesh, int partIndex, model::MaterialPointer material, const Transform& transform, const Transform& offsetTransform) {
|
||||
|
||||
updateMeshPart(mesh, partIndex);
|
||||
updateMaterial(material);
|
||||
updateTransform(transform, offsetTransform);
|
||||
}
|
||||
|
||||
void MeshPartPayload::initCache() {
|
||||
const std::vector<std::unique_ptr<NetworkMesh>>& networkMeshes = model->_geometry->getMeshes();
|
||||
const NetworkMesh& networkMesh = *(networkMeshes.at(meshIndex).get());
|
||||
_drawMesh = networkMesh._mesh;
|
||||
|
||||
const FBXGeometry& geometry = model->_geometry->getFBXGeometry();
|
||||
const FBXMesh& mesh = geometry.meshes.at(meshIndex);
|
||||
_hasColorAttrib = !mesh.colors.isEmpty();
|
||||
_isBlendShaped = !mesh.blendshapes.isEmpty();
|
||||
_isSkinned = !mesh.clusterIndices.isEmpty();
|
||||
|
||||
|
||||
_drawPart = _drawMesh->getPartBuffer().get<model::Mesh::Part>(partIndex);
|
||||
|
||||
auto networkMaterial = model->_geometry->getShapeMaterial(_shapeID);
|
||||
if (networkMaterial) {
|
||||
_drawMaterial = networkMaterial->_material;
|
||||
};
|
||||
void MeshPartPayload::updateMeshPart(model::MeshPointer drawMesh, int partIndex) {
|
||||
_drawMesh = drawMesh;
|
||||
if (_drawMesh) {
|
||||
auto vertexFormat = _drawMesh->getVertexFormat();
|
||||
_hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR);
|
||||
_drawPart = _drawMesh->getPartBuffer().get<model::Mesh::Part>(partIndex);
|
||||
|
||||
_localBound = _drawMesh->evalPartBound(partIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void MeshPartPayload::updateModelLocation(glm::vec3 position, glm::quat orientation) {
|
||||
_modelPosition = position;
|
||||
_modelOrientation = orientation;
|
||||
void MeshPartPayload::updateTransform(const Transform& transform, const Transform& offsetTransform) {
|
||||
_transform = transform;
|
||||
_offsetTransform = offsetTransform;
|
||||
Transform::mult(_drawTransform, _transform, _offsetTransform);
|
||||
_worldBound = _localBound;
|
||||
_worldBound.transform(_drawTransform);
|
||||
}
|
||||
|
||||
void MeshPartPayload::updateMaterial(model::MaterialPointer drawMaterial) {
|
||||
_drawMaterial = drawMaterial;
|
||||
}
|
||||
|
||||
render::ItemKey MeshPartPayload::getKey() const {
|
||||
ItemKey::Builder builder;
|
||||
builder.withTypeShape();
|
||||
|
||||
if (!model->isVisible()) {
|
||||
builder.withInvisible();
|
||||
}
|
||||
|
||||
if (_isBlendShaped || _isSkinned) {
|
||||
builder.withDeformed();
|
||||
}
|
||||
|
||||
if (_drawMaterial) {
|
||||
auto matKey = _drawMaterial->getKey();
|
||||
if (matKey.isTransparent() || matKey.isTransparentMap()) {
|
||||
|
@ -99,9 +84,7 @@ render::ItemKey MeshPartPayload::getKey() const {
|
|||
}
|
||||
|
||||
render::Item::Bound MeshPartPayload::getBound() const {
|
||||
// NOTE: we can't cache this bounds because we need to handle the case of a moving
|
||||
// entity or mesh part.
|
||||
return model->getPartBounds(meshIndex, partIndex, _modelPosition, _modelOrientation);
|
||||
return _worldBound;
|
||||
}
|
||||
|
||||
void MeshPartPayload::drawCall(gpu::Batch& batch) const {
|
||||
|
@ -109,22 +92,12 @@ void MeshPartPayload::drawCall(gpu::Batch& batch) const {
|
|||
}
|
||||
|
||||
void MeshPartPayload::bindMesh(gpu::Batch& batch) const {
|
||||
if (!_isBlendShaped) {
|
||||
batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0);
|
||||
|
||||
batch.setInputFormat((_drawMesh->getVertexFormat()));
|
||||
|
||||
batch.setInputStream(0, _drawMesh->getVertexStream());
|
||||
} else {
|
||||
batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0);
|
||||
batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0);
|
||||
|
||||
batch.setInputFormat((_drawMesh->getVertexFormat()));
|
||||
batch.setInputFormat((_drawMesh->getVertexFormat()));
|
||||
|
||||
batch.setInputStream(0, _drawMesh->getVertexStream());
|
||||
|
||||
batch.setInputBuffer(0, model->_blendedVertexBuffers[meshIndex], 0, sizeof(glm::vec3));
|
||||
batch.setInputBuffer(1, model->_blendedVertexBuffers[meshIndex], _drawMesh->getNumVertices() * sizeof(glm::vec3), sizeof(glm::vec3));
|
||||
batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2));
|
||||
}
|
||||
|
||||
// TODO: Get rid of that extra call
|
||||
if (!_hasColorAttrib) {
|
||||
batch._glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
@ -215,31 +188,208 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ModelRender::Locatio
|
|||
}
|
||||
|
||||
void MeshPartPayload::bindTransform(gpu::Batch& batch, const ModelRender::Locations* locations) const {
|
||||
// Still relying on the raw data from the model
|
||||
const Model::MeshState& state = model->_meshStates.at(meshIndex);
|
||||
|
||||
Transform transform;
|
||||
if (state.clusterBuffer) {
|
||||
if (model->_cauterizeBones) {
|
||||
batch.setUniformBuffer(ModelRender::SKINNING_GPU_SLOT, state.cauterizedClusterBuffer);
|
||||
} else {
|
||||
batch.setUniformBuffer(ModelRender::SKINNING_GPU_SLOT, state.clusterBuffer);
|
||||
}
|
||||
} else {
|
||||
if (model->_cauterizeBones) {
|
||||
transform = Transform(state.cauterizedClusterMatrices[0]);
|
||||
} else {
|
||||
transform = Transform(state.clusterMatrices[0]);
|
||||
}
|
||||
}
|
||||
transform.preTranslate(_modelPosition);
|
||||
batch.setModelTransform(transform);
|
||||
batch.setModelTransform(_drawTransform);
|
||||
}
|
||||
|
||||
|
||||
void MeshPartPayload::render(RenderArgs* args) const {
|
||||
PerformanceTimer perfTimer("MeshPartPayload::render");
|
||||
if (!model->_readyWhenAdded || !model->_isVisible) {
|
||||
|
||||
|
||||
gpu::Batch& batch = *(args->_batch);
|
||||
auto mode = args->_renderMode;
|
||||
|
||||
auto alphaThreshold = args->_alphaThreshold; //translucent ? TRANSPARENT_ALPHA_THRESHOLD : OPAQUE_ALPHA_THRESHOLD; // FIX ME
|
||||
|
||||
model::MaterialKey drawMaterialKey;
|
||||
if (_drawMaterial) {
|
||||
drawMaterialKey = _drawMaterial->getKey();
|
||||
}
|
||||
bool translucentMesh = drawMaterialKey.isTransparent() || drawMaterialKey.isTransparentMap();
|
||||
|
||||
bool hasTangents = drawMaterialKey.isNormalMap();
|
||||
bool hasSpecular = drawMaterialKey.isGlossMap();
|
||||
bool hasLightmap = drawMaterialKey.isLightmapMap();
|
||||
bool isSkinned = false;
|
||||
bool wireframe = false;
|
||||
if (wireframe) {
|
||||
translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false;
|
||||
}
|
||||
|
||||
ModelRender::Locations* locations = nullptr;
|
||||
ModelRender::pickPrograms(batch, mode, translucentMesh, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe,
|
||||
args, locations);
|
||||
|
||||
|
||||
// Bind the model transform and the skinCLusterMatrices if needed
|
||||
bindTransform(batch, locations);
|
||||
|
||||
//Bind the index buffer and vertex buffer and Blend shapes if needed
|
||||
bindMesh(batch);
|
||||
|
||||
// apply material properties
|
||||
bindMaterial(batch, locations);
|
||||
|
||||
|
||||
// TODO: We should be able to do that just in the renderTransparentJob
|
||||
if (translucentMesh && locations->lightBufferUnit >= 0) {
|
||||
PerformanceTimer perfTimer("DLE->setupTransparent()");
|
||||
|
||||
DependencyManager::get<DeferredLightingEffect>()->setupTransparent(args, locations->lightBufferUnit);
|
||||
}
|
||||
if (args) {
|
||||
args->_details._materialSwitches++;
|
||||
}
|
||||
|
||||
// Draw!
|
||||
{
|
||||
PerformanceTimer perfTimer("batch.drawIndexed()");
|
||||
drawCall(batch);
|
||||
}
|
||||
|
||||
if (args) {
|
||||
const int INDICES_PER_TRIANGLE = 3;
|
||||
args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
namespace render {
|
||||
template <> const ItemKey payloadGetKey(const ModelMeshPartPayload::Pointer& payload) {
|
||||
if (payload) {
|
||||
return payload->getKey();
|
||||
}
|
||||
// Return opaque for lack of a better idea
|
||||
return ItemKey::Builder::opaqueShape();
|
||||
}
|
||||
|
||||
template <> const Item::Bound payloadGetBound(const ModelMeshPartPayload::Pointer& payload) {
|
||||
if (payload) {
|
||||
return payload->getBound();
|
||||
}
|
||||
return render::Item::Bound();
|
||||
}
|
||||
template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, RenderArgs* args) {
|
||||
return payload->render(args);
|
||||
}
|
||||
}
|
||||
|
||||
using namespace render;
|
||||
|
||||
ModelMeshPartPayload::ModelMeshPartPayload(Model* model, int _meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) :
|
||||
_model(model),
|
||||
_meshIndex(_meshIndex),
|
||||
_shapeID(shapeIndex) {
|
||||
auto& modelMesh = _model->_geometry->getMeshes().at(_meshIndex)->_mesh;
|
||||
updateMeshPart(modelMesh, partIndex);
|
||||
|
||||
updateTransform(transform, offsetTransform);
|
||||
initCache();
|
||||
}
|
||||
|
||||
void ModelMeshPartPayload::initCache() {
|
||||
if (_drawMesh) {
|
||||
auto vertexFormat = _drawMesh->getVertexFormat();
|
||||
_hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR);
|
||||
_isSkinned = vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT) && vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_INDEX);
|
||||
|
||||
|
||||
const FBXGeometry& geometry = _model->_geometry->getFBXGeometry();
|
||||
const FBXMesh& mesh = geometry.meshes.at(_meshIndex);
|
||||
_isBlendShaped = !mesh.blendshapes.isEmpty();
|
||||
}
|
||||
|
||||
auto networkMaterial = _model->_geometry->getShapeMaterial(_shapeID);
|
||||
if (networkMaterial) {
|
||||
_drawMaterial = networkMaterial->_material;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
void ModelMeshPartPayload::notifyLocationChanged() {
|
||||
_model->_needsUpdateClusterMatrices = true;
|
||||
}
|
||||
|
||||
render::ItemKey ModelMeshPartPayload::getKey() const {
|
||||
ItemKey::Builder builder;
|
||||
builder.withTypeShape();
|
||||
|
||||
if (!_model->isVisible()) {
|
||||
builder.withInvisible();
|
||||
}
|
||||
|
||||
if (_isBlendShaped || _isSkinned) {
|
||||
builder.withDeformed();
|
||||
}
|
||||
|
||||
if (_drawMaterial) {
|
||||
auto matKey = _drawMaterial->getKey();
|
||||
if (matKey.isTransparent() || matKey.isTransparentMap()) {
|
||||
builder.withTransparent();
|
||||
}
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
render::Item::Bound ModelMeshPartPayload::getBound() const {
|
||||
// NOTE: we can't cache this bounds because we need to handle the case of a moving
|
||||
// entity or mesh part.
|
||||
return _model->getPartBounds(_meshIndex, _partIndex, _transform.getTranslation(), _transform.getRotation());
|
||||
}
|
||||
|
||||
void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) const {
|
||||
if (!_isBlendShaped) {
|
||||
batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0);
|
||||
|
||||
batch.setInputFormat((_drawMesh->getVertexFormat()));
|
||||
|
||||
batch.setInputStream(0, _drawMesh->getVertexStream());
|
||||
} else {
|
||||
batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0);
|
||||
|
||||
batch.setInputFormat((_drawMesh->getVertexFormat()));
|
||||
|
||||
batch.setInputBuffer(0, _model->_blendedVertexBuffers[_meshIndex], 0, sizeof(glm::vec3));
|
||||
batch.setInputBuffer(1, _model->_blendedVertexBuffers[_meshIndex], _drawMesh->getNumVertices() * sizeof(glm::vec3), sizeof(glm::vec3));
|
||||
batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2));
|
||||
}
|
||||
|
||||
// TODO: Get rid of that extra call
|
||||
if (!_hasColorAttrib) {
|
||||
batch._glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ModelRender::Locations* locations) const {
|
||||
// Still relying on the raw data from the model
|
||||
const Model::MeshState& state = _model->_meshStates.at(_meshIndex);
|
||||
|
||||
Transform transform;
|
||||
if (state.clusterBuffer) {
|
||||
if (_model->_cauterizeBones) {
|
||||
batch.setUniformBuffer(ModelRender::SKINNING_GPU_SLOT, state.cauterizedClusterBuffer);
|
||||
} else {
|
||||
batch.setUniformBuffer(ModelRender::SKINNING_GPU_SLOT, state.clusterBuffer);
|
||||
}
|
||||
} else {
|
||||
if (_model->_cauterizeBones) {
|
||||
transform = Transform(state.cauterizedClusterMatrices[0]);
|
||||
} else {
|
||||
transform = Transform(state.clusterMatrices[0]);
|
||||
}
|
||||
}
|
||||
// transform.preTranslate(_modelPosition);
|
||||
transform.preTranslate(_transform.getTranslation());
|
||||
batch.setModelTransform(transform);
|
||||
}
|
||||
|
||||
|
||||
void ModelMeshPartPayload::render(RenderArgs* args) const {
|
||||
PerformanceTimer perfTimer("ModelMeshPartPayload::render");
|
||||
if (!_model->_readyWhenAdded || !_model->_isVisible) {
|
||||
return; // bail asap
|
||||
}
|
||||
|
||||
|
@ -248,25 +398,25 @@ void MeshPartPayload::render(RenderArgs* args) const {
|
|||
|
||||
auto alphaThreshold = args->_alphaThreshold; //translucent ? TRANSPARENT_ALPHA_THRESHOLD : OPAQUE_ALPHA_THRESHOLD; // FIX ME
|
||||
|
||||
const FBXGeometry& geometry = model->_geometry->getFBXGeometry();
|
||||
const std::vector<std::unique_ptr<NetworkMesh>>& networkMeshes = model->_geometry->getMeshes();
|
||||
const FBXGeometry& geometry = _model->_geometry->getFBXGeometry();
|
||||
const std::vector<std::unique_ptr<NetworkMesh>>& networkMeshes = _model->_geometry->getMeshes();
|
||||
|
||||
// guard against partially loaded meshes
|
||||
if (meshIndex >= (int)networkMeshes.size() || meshIndex >= (int)geometry.meshes.size() || meshIndex >= (int)model->_meshStates.size() ) {
|
||||
if (_meshIndex >= (int)networkMeshes.size() || _meshIndex >= (int)geometry.meshes.size() || _meshIndex >= (int)_model->_meshStates.size() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Back to model to update the cluster matrices right now
|
||||
model->updateClusterMatrices(_modelPosition, _modelOrientation);
|
||||
_model->updateClusterMatrices(_transform.getTranslation(), _transform.getRotation());
|
||||
|
||||
const FBXMesh& mesh = geometry.meshes.at(meshIndex);
|
||||
const FBXMesh& mesh = geometry.meshes.at(_meshIndex);
|
||||
|
||||
// if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown
|
||||
// to false to rebuild out mesh groups.
|
||||
if (meshIndex < 0 || meshIndex >= (int)networkMeshes.size() || meshIndex > geometry.meshes.size()) {
|
||||
model->_meshGroupsKnown = false; // regenerate these lists next time around.
|
||||
model->_readyWhenAdded = false; // in case any of our users are using scenes
|
||||
model->invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid
|
||||
if (_meshIndex < 0 || _meshIndex >= (int)networkMeshes.size() || _meshIndex > geometry.meshes.size()) {
|
||||
_model->_meshGroupsKnown = false; // regenerate these lists next time around.
|
||||
_model->_readyWhenAdded = false; // in case any of our users are using scenes
|
||||
_model->invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid
|
||||
return; // FIXME!
|
||||
}
|
||||
|
||||
|
@ -276,13 +426,7 @@ void MeshPartPayload::render(RenderArgs* args) const {
|
|||
// sanity check
|
||||
return; // FIXME!
|
||||
}
|
||||
|
||||
|
||||
// guard against partially loaded meshes
|
||||
if (partIndex >= mesh.parts.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
model::MaterialKey drawMaterialKey;
|
||||
if (_drawMaterial) {
|
||||
drawMaterialKey = _drawMaterial->getKey();
|
||||
|
@ -293,12 +437,12 @@ void MeshPartPayload::render(RenderArgs* args) const {
|
|||
bool hasSpecular = drawMaterialKey.isGlossMap();
|
||||
bool hasLightmap = drawMaterialKey.isLightmapMap();
|
||||
bool isSkinned = _isSkinned;
|
||||
bool wireframe = model->isWireframe();
|
||||
bool wireframe = _model->isWireframe();
|
||||
|
||||
// render the part bounding box
|
||||
#ifdef DEBUG_BOUNDING_PARTS
|
||||
{
|
||||
AABox partBounds = getPartBounds(meshIndex, partIndex);
|
||||
AABox partBounds = getPartBounds(_meshIndex, partIndex);
|
||||
bool inView = args->_viewFrustum->boxInFrustum(partBounds) != ViewFrustum::OUTSIDE;
|
||||
|
||||
glm::vec4 cubeColor;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// MeshPartPayload.h
|
||||
// ModelMeshPartPayload.h
|
||||
// interface/src/renderer
|
||||
//
|
||||
// Created by Sam Gateau on 10/3/15.
|
||||
|
@ -24,41 +24,44 @@ class Model;
|
|||
|
||||
class MeshPartPayload {
|
||||
public:
|
||||
MeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, glm::vec3 position, glm::quat orientation);
|
||||
|
||||
MeshPartPayload() {}
|
||||
MeshPartPayload(model::MeshPointer mesh, int partIndex, model::MaterialPointer material, const Transform& transform, const Transform& offsetTransform);
|
||||
|
||||
typedef render::Payload<MeshPartPayload> Payload;
|
||||
typedef Payload::DataPointer Pointer;
|
||||
|
||||
Model* model;
|
||||
int meshIndex;
|
||||
int partIndex;
|
||||
int _shapeID;
|
||||
glm::vec3 _modelPosition;
|
||||
glm::quat _modelOrientation;
|
||||
virtual void updateMeshPart(model::MeshPointer drawMesh, int partIndex);
|
||||
|
||||
void updateModelLocation(glm::vec3 position, glm::quat orientation);
|
||||
virtual void notifyLocationChanged() {}
|
||||
virtual void updateTransform(const Transform& transform, const Transform& offsetTransform);
|
||||
|
||||
virtual void updateMaterial(model::MaterialPointer drawMaterial);
|
||||
|
||||
// Render Item interface
|
||||
render::ItemKey getKey() const;
|
||||
render::Item::Bound getBound() const;
|
||||
void render(RenderArgs* args) const;
|
||||
|
||||
// MeshPartPayload functions to perform render
|
||||
virtual render::ItemKey getKey() const;
|
||||
virtual render::Item::Bound getBound() const;
|
||||
virtual void render(RenderArgs* args) const;
|
||||
|
||||
// ModelMeshPartPayload functions to perform render
|
||||
void drawCall(gpu::Batch& batch) const;
|
||||
void bindMesh(gpu::Batch& batch) const;
|
||||
void bindMaterial(gpu::Batch& batch, const ModelRender::Locations* locations) const;
|
||||
void bindTransform(gpu::Batch& batch, const ModelRender::Locations* locations) const;
|
||||
|
||||
|
||||
void initCache();
|
||||
virtual void bindMesh(gpu::Batch& batch) const;
|
||||
virtual void bindMaterial(gpu::Batch& batch, const ModelRender::Locations* locations) const;
|
||||
virtual void bindTransform(gpu::Batch& batch, const ModelRender::Locations* locations) const;
|
||||
|
||||
// Payload resource cached values
|
||||
model::MeshPointer _drawMesh;
|
||||
int _partIndex = 0;
|
||||
model::Mesh::Part _drawPart;
|
||||
|
||||
model::MaterialPointer _drawMaterial;
|
||||
|
||||
model::Box _localBound;
|
||||
Transform _drawTransform;
|
||||
Transform _transform;
|
||||
Transform _offsetTransform;
|
||||
mutable model::Box _worldBound;
|
||||
|
||||
bool _hasColorAttrib = false;
|
||||
bool _isSkinned = false;
|
||||
bool _isBlendShaped = false;
|
||||
};
|
||||
|
||||
namespace render {
|
||||
|
@ -67,4 +70,32 @@ namespace render {
|
|||
template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderArgs* args);
|
||||
}
|
||||
|
||||
|
||||
class ModelMeshPartPayload : public MeshPartPayload {
|
||||
public:
|
||||
ModelMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform);
|
||||
|
||||
typedef render::Payload<ModelMeshPartPayload> Payload;
|
||||
typedef Payload::DataPointer Pointer;
|
||||
|
||||
void notifyLocationChanged() override;
|
||||
|
||||
// Render Item interface
|
||||
render::ItemKey getKey() const override;
|
||||
render::Item::Bound getBound() const override;
|
||||
void render(RenderArgs* args) const override;
|
||||
|
||||
// ModelMeshPartPayload functions to perform render
|
||||
void bindMesh(gpu::Batch& batch) const override;
|
||||
void bindTransform(gpu::Batch& batch, const ModelRender::Locations* locations) const override;
|
||||
|
||||
|
||||
void initCache();
|
||||
Model* _model;
|
||||
int _meshIndex;
|
||||
int _shapeID;
|
||||
bool _isSkinned = false;
|
||||
bool _isBlendShaped = false;
|
||||
};
|
||||
|
||||
#endif // hifi_MeshPartPayload_h
|
||||
|
|
|
@ -90,7 +90,7 @@ void Model::setScale(const glm::vec3& scale) {
|
|||
_scaledToFit = false;
|
||||
}
|
||||
|
||||
const float METERS_PER_MILLIMETER = 0.01f;
|
||||
const float METERS_PER_MILLIMETER = 0.01f;
|
||||
|
||||
void Model::setScaleInternal(const glm::vec3& scale) {
|
||||
if (glm::distance(_scale, scale) > METERS_PER_MILLIMETER) {
|
||||
|
@ -110,11 +110,19 @@ void Model::setOffset(const glm::vec3& offset) {
|
|||
void Model::enqueueLocationChange() {
|
||||
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||
|
||||
Transform transform;
|
||||
transform.setTranslation(_translation);
|
||||
transform.setRotation(_rotation);
|
||||
|
||||
Transform offset;
|
||||
offset.setScale(_scale);
|
||||
offset.postTranslate(_offset);
|
||||
|
||||
render::PendingChanges pendingChanges;
|
||||
foreach (auto itemID, _renderItems.keys()) {
|
||||
pendingChanges.updateItem<MeshPartPayload>(itemID, [=](MeshPartPayload& data) {
|
||||
data.updateModelLocation(_translation, _rotation);
|
||||
data.model->_needsUpdateClusterMatrices = true;
|
||||
data.updateTransform(transform, offset);
|
||||
data.notifyLocationChanged();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -495,11 +503,10 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene, render::PendingChan
|
|||
|
||||
foreach (auto renderItem, _renderItemsSet) {
|
||||
auto item = scene->allocateID();
|
||||
auto renderData = MeshPartPayload::Pointer(renderItem);
|
||||
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderData);
|
||||
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
|
||||
pendingChanges.resetItem(item, renderPayload);
|
||||
pendingChanges.updateItem<MeshPartPayload>(item, [&](MeshPartPayload& data) {
|
||||
data.model->_needsUpdateClusterMatrices = true;
|
||||
data.notifyLocationChanged();
|
||||
});
|
||||
_renderItems.insert(item, renderPayload);
|
||||
somethingAdded = true;
|
||||
|
@ -523,12 +530,11 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene,
|
|||
|
||||
foreach (auto renderItem, _renderItemsSet) {
|
||||
auto item = scene->allocateID();
|
||||
auto renderData = MeshPartPayload::Pointer(renderItem);
|
||||
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderData);
|
||||
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
|
||||
renderPayload->addStatusGetters(statusGetters);
|
||||
pendingChanges.resetItem(item, renderPayload);
|
||||
pendingChanges.updateItem<MeshPartPayload>(item, [&](MeshPartPayload& data) {
|
||||
data.model->_needsUpdateClusterMatrices = true;
|
||||
data.notifyLocationChanged();
|
||||
});
|
||||
_renderItems.insert(item, renderPayload);
|
||||
somethingAdded = true;
|
||||
|
@ -1127,8 +1133,14 @@ AABox Model::getPartBounds(int meshIndex, int partIndex, glm::vec3 modelPosition
|
|||
|
||||
void Model::segregateMeshGroups() {
|
||||
QSharedPointer<NetworkGeometry> networkGeometry;
|
||||
if (_showCollisionHull && _collisionGeometry && _collisionGeometry->isLoaded()) {
|
||||
networkGeometry = _collisionGeometry;
|
||||
bool showingCollisionHull = false;
|
||||
if (_showCollisionHull && _collisionGeometry) {
|
||||
if (_collisionGeometry->isLoaded()) {
|
||||
networkGeometry = _collisionGeometry;
|
||||
showingCollisionHull = true;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
networkGeometry = _geometry;
|
||||
}
|
||||
|
@ -1136,8 +1148,10 @@ void Model::segregateMeshGroups() {
|
|||
const std::vector<std::unique_ptr<NetworkMesh>>& networkMeshes = networkGeometry->getMeshes();
|
||||
|
||||
// all of our mesh vectors must match in size
|
||||
if ((int)networkMeshes.size() != geometry.meshes.size() ||
|
||||
geometry.meshes.size() != _meshStates.size()) {
|
||||
auto geoMeshesSize = geometry.meshes.size();
|
||||
if ((int)networkMeshes.size() != geoMeshesSize ||
|
||||
// geometry.meshes.size() != _meshStates.size()) {
|
||||
geoMeshesSize > _meshStates.size()) {
|
||||
qDebug() << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet.";
|
||||
return;
|
||||
}
|
||||
|
@ -1147,15 +1161,30 @@ void Model::segregateMeshGroups() {
|
|||
|
||||
_renderItemsSet.clear();
|
||||
|
||||
Transform transform;
|
||||
transform.setTranslation(_translation);
|
||||
transform.setRotation(_rotation);
|
||||
|
||||
Transform offset;
|
||||
offset.setScale(_scale);
|
||||
offset.postTranslate(_offset);
|
||||
|
||||
// Run through all of the meshes, and place them into their segregated, but unsorted buckets
|
||||
int shapeID = 0;
|
||||
for (int i = 0; i < (int)networkMeshes.size(); i++) {
|
||||
const FBXMesh& mesh = geometry.meshes.at(i);
|
||||
const NetworkMesh& networkMesh = *(networkMeshes.at(i).get());
|
||||
|
||||
// Create the render payloads
|
||||
int totalParts = mesh.parts.size();
|
||||
for (int partIndex = 0; partIndex < totalParts; partIndex++) {
|
||||
_renderItemsSet << std::make_shared<MeshPartPayload>(this, i, partIndex, shapeID, _translation, _rotation);
|
||||
if (showingCollisionHull) {
|
||||
_renderItemsSet << std::make_shared<MeshPartPayload>(networkMesh._mesh, partIndex, ModelRender::getCollisionHullMaterial(), transform, offset);
|
||||
|
||||
} else {
|
||||
_renderItemsSet << std::make_shared<ModelMeshPartPayload>(this, i, partIndex, shapeID, transform, offset);
|
||||
}
|
||||
|
||||
shapeID++;
|
||||
}
|
||||
}
|
||||
|
@ -1168,15 +1197,22 @@ bool Model::initWhenReady(render::ScenePointer scene) {
|
|||
|
||||
render::PendingChanges pendingChanges;
|
||||
|
||||
Transform transform;
|
||||
transform.setTranslation(_translation);
|
||||
transform.setRotation(_rotation);
|
||||
|
||||
Transform offset;
|
||||
offset.setScale(_scale);
|
||||
offset.postTranslate(_offset);
|
||||
|
||||
foreach (auto renderItem, _renderItemsSet) {
|
||||
auto item = scene->allocateID();
|
||||
auto renderData = MeshPartPayload::Pointer(renderItem);
|
||||
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderData);
|
||||
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
|
||||
_renderItems.insert(item, renderPayload);
|
||||
pendingChanges.resetItem(item, renderPayload);
|
||||
pendingChanges.updateItem<MeshPartPayload>(item, [&](MeshPartPayload& data) {
|
||||
data.updateModelLocation(_translation, _rotation);
|
||||
data.model->_needsUpdateClusterMatrices = true;
|
||||
data.updateTransform(transform, offset);
|
||||
data.notifyLocationChanged();
|
||||
});
|
||||
}
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
|
|
|
@ -366,7 +366,7 @@ private:
|
|||
bool _needsUpdateClusterMatrices = true;
|
||||
bool _showCollisionHull = false;
|
||||
|
||||
friend class MeshPartPayload;
|
||||
friend class ModelMeshPartPayload;
|
||||
protected:
|
||||
RigPointer _rig;
|
||||
};
|
||||
|
|
|
@ -280,3 +280,16 @@ void ModelRender::pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, b
|
|||
DependencyManager::get<TextureCache>()->getNormalFittingTexture());
|
||||
}
|
||||
}
|
||||
|
||||
model::MaterialPointer ModelRender::_collisionHullMaterial;
|
||||
|
||||
model::MaterialPointer ModelRender::getCollisionHullMaterial() {
|
||||
if (!_collisionHullMaterial) {
|
||||
_collisionHullMaterial = std::make_shared<model::Material>();
|
||||
_collisionHullMaterial->setDiffuse(glm::vec3(1.0f, 0.5f, 0.0f));
|
||||
_collisionHullMaterial->setMetallic(0.02f);
|
||||
_collisionHullMaterial->setGloss(1.0f);
|
||||
}
|
||||
return _collisionHullMaterial;
|
||||
}
|
||||
|
||||
|
|
|
@ -149,6 +149,11 @@ public:
|
|||
|
||||
static const RenderPipelineLib& getRenderPipelineLib();
|
||||
|
||||
// Collision hull Material
|
||||
static model::MaterialPointer _collisionHullMaterial;
|
||||
|
||||
static model::MaterialPointer getCollisionHullMaterial();
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_ModelRender_h
|
|
@ -12,6 +12,7 @@
|
|||
#include "AABox.h"
|
||||
|
||||
#include "AACube.h"
|
||||
#include "Transform.h"
|
||||
#include "Extents.h"
|
||||
#include "GeometryUtil.h"
|
||||
#include "NumericalConstants.h"
|
||||
|
@ -42,11 +43,6 @@ glm::vec3 AABox::calcCenter() const {
|
|||
return center;
|
||||
}
|
||||
|
||||
void AABox::scale(float scale) {
|
||||
_corner = _corner * scale;
|
||||
_scale = _scale * scale;
|
||||
}
|
||||
|
||||
glm::vec3 AABox::getVertex(BoxVertex vertex) const {
|
||||
switch (vertex) {
|
||||
case BOTTOM_LEFT_NEAR:
|
||||
|
@ -486,3 +482,59 @@ AABox& AABox::operator += (const AABox& box) {
|
|||
}
|
||||
return (*this);
|
||||
}
|
||||
|
||||
void AABox::scale(const glm::vec3& scale) {
|
||||
_corner *= 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;
|
||||
}
|
||||
|
||||
void AABox::transform(const Transform& transform) {
|
||||
scale(transform.getScale());
|
||||
rotate(transform.getRotation());
|
||||
translate(transform.getTranslation());
|
||||
}
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
class AACube;
|
||||
class Extents;
|
||||
class Transform;
|
||||
|
||||
class AABox {
|
||||
|
||||
|
@ -34,13 +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;
|
||||
void scale(float scale);
|
||||
|
||||
const glm::vec3& getCorner() const { return _corner; }
|
||||
const glm::vec3& getScale() const { return _scale; }
|
||||
const glm::vec3& getDimensions() const { return _scale; }
|
||||
|
@ -80,6 +81,20 @@ public:
|
|||
AABox& operator += (const glm::vec3& point);
|
||||
AABox& operator += (const AABox& box);
|
||||
|
||||
// Translate the AABox just moving the corner
|
||||
void translate(const glm::vec3& translation) { _corner += translation; }
|
||||
|
||||
// Rotate the AABox around its frame origin
|
||||
// meaning rotating the corners of the AABox around the point {0,0,0} and reevaluating the min max
|
||||
void rotate(const glm::quat& rotation);
|
||||
|
||||
/// Scale the AABox
|
||||
void scale(float scale);
|
||||
void scale(const glm::vec3& scale);
|
||||
|
||||
// Transform the extents with transform
|
||||
void transform(const Transform& transform);
|
||||
|
||||
bool isInvalid() const { return _corner == glm::vec3(std::numeric_limits<float>::infinity()); }
|
||||
|
||||
private:
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
12
plugins/hifiSdl2/CMakeLists.txt
Normal file
12
plugins/hifiSdl2/CMakeLists.txt
Normal 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 hifiSdl2)
|
||||
setup_hifi_plugin(Script Qml Widgets)
|
||||
link_hifi_libraries(shared controllers plugins input-plugins script-engine)
|
||||
target_sdl2()
|
|
@ -11,8 +11,6 @@
|
|||
|
||||
#include <qapplication.h>
|
||||
|
||||
#include <HFActionEvent.h>
|
||||
#include <HFBackEvent.h>
|
||||
#include <PerfStat.h>
|
||||
#include <controllers/UserInputMapper.h>
|
||||
|
||||
|
@ -163,6 +161,7 @@ void SDL2Manager::pluginUpdate(float deltaTime, bool jointsCaptured) {
|
|||
joystick->handleButtonEvent(event.cbutton);
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (event.cbutton.button == SDL_CONTROLLER_BUTTON_BACK) {
|
||||
// this will either start or stop a global back event
|
||||
QEvent::Type backType = (event.type == SDL_CONTROLLERBUTTONDOWN)
|
||||
|
@ -172,6 +171,7 @@ void SDL2Manager::pluginUpdate(float deltaTime, bool jointsCaptured) {
|
|||
|
||||
qApp->sendEvent(qApp, &backEvent);
|
||||
}
|
||||
#endif
|
||||
|
||||
} else if (event.type == SDL_CONTROLLERDEVICEADDED) {
|
||||
SDL_GameController* controller = SDL_GameControllerOpen(event.cdevice.which);
|
|
@ -17,8 +17,7 @@
|
|||
#endif
|
||||
|
||||
#include <controllers/UserInputMapper.h>
|
||||
|
||||
#include "InputPlugin.h"
|
||||
#include <input-plugins/InputPlugin.h>
|
||||
#include "Joystick.h"
|
||||
|
||||
class SDL2Manager : public InputPlugin {
|
44
plugins/hifiSdl2/src/SDL2Provider.cpp
Normal file
44
plugins/hifiSdl2/src/SDL2Provider.cpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
//
|
||||
// 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 "SDL2Manager.h"
|
||||
|
||||
class SDL2Provider : public QObject, public InputProvider {
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID InputProvider_iid FILE "plugin.json")
|
||||
Q_INTERFACES(InputProvider)
|
||||
|
||||
public:
|
||||
SDL2Provider(QObject* parent = nullptr) : QObject(parent) {}
|
||||
virtual ~SDL2Provider() {}
|
||||
|
||||
virtual InputPluginList getInputPlugins() override {
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [&] {
|
||||
InputPluginPointer plugin(new SDL2Manager());
|
||||
if (plugin->isSupported()) {
|
||||
_inputPlugins.push_back(plugin);
|
||||
}
|
||||
});
|
||||
return _inputPlugins;
|
||||
}
|
||||
|
||||
private:
|
||||
InputPluginList _inputPlugins;
|
||||
};
|
||||
|
||||
#include "SDL2Provider.moc"
|
1
plugins/hifiSdl2/src/plugin.json
Normal file
1
plugins/hifiSdl2/src/plugin.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
Loading…
Reference in a new issue