// TODO: we need a solution for changes to the postion/rotation/etc of a model... // this current code path only addresses that in this setup case... not the changing/moving case bool RenderableModelEntityItem::readyToAddToScene(RenderArgs* renderArgs) { if (!_model && renderArgs) { // TODO: this getModel() appears to be about 3% of model render time. We should optimize PerformanceTimer perfTimer("getModel"); EntityTreeRenderer* renderer = static_cast<EntityTreeRenderer*>(renderArgs->_renderer); getModel(renderer); } if (renderArgs && _model && _needsInitialSimulation && _model->isActive() && _model->isLoaded()) { _model->setScaleToFit(true, getDimensions()); _model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); _model->setRotation(getRotation()); _model->setTranslation(getPosition()); // make sure to simulate so everything gets set up correctly for rendering { PerformanceTimer perfTimer("_model->simulate"); _model->simulate(0.0f); } _needsInitialSimulation = false; _model->renderSetup(renderArgs); } bool ready = !_needsInitialSimulation && _model && _model->readyToAddToScene(renderArgs); return ready; }
bool SphereEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, void** intersectedObject, bool precisionPicking) const { // determine the ray in the frame of the entity transformed from a unit sphere glm::mat4 translation = glm::translate(getPosition()); glm::mat4 rotation = glm::mat4_cast(getRotation()); glm::mat4 scale = glm::scale(getDimensions()); glm::mat4 registration = glm::translate(glm::vec3(0.5f, 0.5f, 0.5f) - getRegistrationPoint()); glm::mat4 entityToWorldMatrix = translation * rotation * scale * registration; glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix); glm::vec3 entityFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f)); glm::vec3 entityFrameDirection = glm::normalize(glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f))); float localDistance; // NOTE: unit sphere has center of 0,0,0 and radius of 0.5 if (findRaySphereIntersection(entityFrameOrigin, entityFrameDirection, glm::vec3(0.0f), 0.5f, localDistance)) { // determine where on the unit sphere the hit point occured glm::vec3 entityFrameHitAt = entityFrameOrigin + (entityFrameDirection * localDistance); // then translate back to work coordinates glm::vec3 hitAt = glm::vec3(entityToWorldMatrix * glm::vec4(entityFrameHitAt, 1.0f)); distance = glm::distance(origin,hitAt); return true; } return false; }
void RenderableZoneEntityItem::initialSimulation() { _model->setScaleToFit(true, getDimensions()); _model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); _model->setRotation(getRotation()); _model->setTranslation(getPosition()); _model->simulate(0.0f); _needsInitialSimulation = false; }
void RenderableModelEntityItem::doInitialModelSimulation() { // The machinery for updateModelBounds will give existing models the opportunity to fix their // translation/rotation/scale/registration. The first two are straightforward, but the latter two have guards to // make sure they don't happen after they've already been set. Here we reset those guards. This doesn't cause the // entity values to change -- it just allows the model to match once it comes in. _model->setScaleToFit(false, getDimensions()); _model->setSnapModelToRegistrationPoint(false, getRegistrationPoint()); // now recalculate the bounds and registration _model->setScaleToFit(true, getDimensions()); _model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); _model->setRotation(getRotation()); _model->setTranslation(getPosition()); { PerformanceTimer perfTimer("_model->simulate"); _model->simulate(0.0f); } _needsInitialSimulation = false; }
bool WebEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, void** intersectedObject, bool precisionPicking) const { glm::vec3 dimensions = getDimensions(); glm::vec2 xyDimensions(dimensions.x, dimensions.y); glm::quat rotation = getRotation(); glm::vec3 position = getPosition() + rotation * (dimensions * (getRegistrationPoint() - ENTITY_ITEM_DEFAULT_REGISTRATION_POINT)); // FIXME - should set face and surfaceNormal return findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance); }
bool TextEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking) const { glm::vec3 dimensions = getScaledDimensions(); glm::vec2 xyDimensions(dimensions.x, dimensions.y); glm::quat rotation = getWorldOrientation(); glm::vec3 position = getWorldPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); // FIXME - should set face and surfaceNormal return findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance); }
void RenderableZoneEntityItem::updateGeometry() { if (_model && !_model->isActive() && hasCompoundShapeURL()) { // Since we have a delayload, we need to update the geometry if it has been downloaded _model->setURL(getCompoundShapeURL()); } if (_model && _model->isActive() && _needsInitialSimulation) { _model->setScaleToFit(true, getDimensions()); _model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); _model->setRotation(getRotation()); _model->setTranslation(getPosition()); _model->simulate(0.0f); _needsInitialSimulation = false; } }
void RenderableModelEntityItem::updateModelBounds() { if (!hasModel() || !_model) { return; } bool movingOrAnimating = isMovingRelativeToParent() || isAnimatingSomething(); glm::vec3 dimensions = getDimensions(); if ((movingOrAnimating || _needsInitialSimulation || _needsJointSimulation || _model->getTranslation() != getPosition() || _model->getScaleToFitDimensions() != dimensions || _model->getRotation() != getRotation() || _model->getRegistrationPoint() != getRegistrationPoint()) && _model->isActive() && _dimensionsInitialized) { doInitialModelSimulation(); _needsJointSimulation = false; } }
bool TextEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, void** intersectedObject, bool precisionPicking) const { RayIntersectionInfo rayInfo; rayInfo._rayStart = origin; rayInfo._rayDirection = direction; rayInfo._rayLength = std::numeric_limits<float>::max(); PlaneShape plane; const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f); glm::vec3 normal = getRotation() * UNROTATED_NORMAL; plane.setNormal(normal); plane.setPoint(getPosition()); // the position is definitely a point on our plane bool intersects = plane.findRayIntersection(rayInfo); if (intersects) { glm::vec3 hitAt = origin + (direction * rayInfo._hitDistance); // now we know the point the ray hit our plane glm::mat4 rotation = glm::mat4_cast(getRotation()); glm::mat4 translation = glm::translate(getPosition()); glm::mat4 entityToWorldMatrix = translation * rotation; glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix); glm::vec3 dimensions = getDimensions(); glm::vec3 registrationPoint = getRegistrationPoint(); glm::vec3 corner = -(dimensions * registrationPoint); AABox entityFrameBox(corner, dimensions); glm::vec3 entityFrameHitAt = glm::vec3(worldToEntityMatrix * glm::vec4(hitAt, 1.0f)); intersects = entityFrameBox.contains(entityFrameHitAt); } if (intersects) { distance = rayInfo._hitDistance; } return intersects; }
bool ZoneEntityItem::contains(const glm::vec3& point) const { GeometryResource::Pointer resource = _shapeResource; if (_shapeType == SHAPE_TYPE_COMPOUND && resource) { if (resource->isLoaded()) { const HFMModel& hfmModel = resource->getHFMModel(); Extents meshExtents = hfmModel.getMeshExtents(); glm::vec3 meshExtentsDiagonal = meshExtents.maximum - meshExtents.minimum; glm::vec3 offset = -meshExtents.minimum- (meshExtentsDiagonal * getRegistrationPoint()); glm::vec3 scale(getScaledDimensions() / meshExtentsDiagonal); glm::mat4 hfmToEntityMatrix = glm::scale(scale) * glm::translate(offset); glm::mat4 entityToWorldMatrix = getTransform().getMatrix(); glm::mat4 worldToHFMMatrix = glm::inverse(entityToWorldMatrix * hfmToEntityMatrix); return hfmModel.convexHullContains(glm::vec3(worldToHFMMatrix * glm::vec4(point, 1.0f))); } } return EntityItem::contains(point); }
glm::vec3 RayPick::intersectRayWithEntityXYPlane(const QUuid& entityID, const glm::vec3& origin, const glm::vec3& direction) { auto props = DependencyManager::get<EntityScriptingInterface>()->getEntityProperties(entityID); return intersectRayWithXYPlane(origin, direction, props.getPosition(), props.getRotation(), props.getRegistrationPoint()); }
glm::vec2 RayPick::projectOntoEntityXYPlane(const QUuid& entityID, const glm::vec3& worldPos, bool unNormalized) { auto props = DependencyManager::get<EntityScriptingInterface>()->getEntityProperties(entityID); return projectOntoXYPlane(worldPos, props.getPosition(), props.getRotation(), props.getDimensions(), props.getRegistrationPoint(), unNormalized); }
OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData) const { // ALL this fits... // object ID [16 bytes] // ByteCountCoded(type code) [~1 byte] // last edited [8 bytes] // ByteCountCoded(last_edited to last_updated delta) [~1-8 bytes] // PropertyFlags<>( everything ) [1-2 bytes] // ~27-35 bytes... OctreeElement::AppendState appendState = OctreeElement::COMPLETED; // assume the best // encode our ID as a byte count coded byte stream QByteArray encodedID = getID().toRfc4122(); // encode our type as a byte count coded byte stream ByteCountCoded<quint32> typeCoder = getType(); QByteArray encodedType = typeCoder; quint64 updateDelta = getLastUpdated() <= getLastEdited() ? 0 : getLastUpdated() - getLastEdited(); ByteCountCoded<quint64> updateDeltaCoder = updateDelta; QByteArray encodedUpdateDelta = updateDeltaCoder; EntityPropertyFlags propertyFlags(PROP_LAST_ITEM); EntityPropertyFlags requestedProperties = getEntityProperties(params); EntityPropertyFlags propertiesDidntFit = requestedProperties; // If we are being called for a subsequent pass at appendEntityData() that failed to completely encode this item, // then our entityTreeElementExtraEncodeData should include data about which properties we need to append. if (entityTreeElementExtraEncodeData && entityTreeElementExtraEncodeData->entities.contains(getEntityItemID())) { requestedProperties = entityTreeElementExtraEncodeData->entities.value(getEntityItemID()); } LevelDetails entityLevel = packetData->startLevel(); quint64 lastEdited = getLastEdited(); const bool wantDebug = false; if (wantDebug) { float editedAgo = getEditedAgo(); QString agoAsString = formatSecondsElapsed(editedAgo); qDebug() << "Writing entity " << getEntityItemID() << " to buffer, lastEdited =" << lastEdited << " ago=" << editedAgo << "seconds - " << agoAsString; } bool successIDFits = false; bool successTypeFits = false; bool successCreatedFits = false; bool successLastEditedFits = false; bool successLastUpdatedFits = false; bool successPropertyFlagsFits = false; int propertyFlagsOffset = 0; int oldPropertyFlagsLength = 0; QByteArray encodedPropertyFlags; int propertyCount = 0; successIDFits = packetData->appendValue(encodedID); if (successIDFits) { successTypeFits = packetData->appendValue(encodedType); } if (successTypeFits) { successCreatedFits = packetData->appendValue(_created); } if (successCreatedFits) { successLastEditedFits = packetData->appendValue(lastEdited); } if (successLastEditedFits) { successLastUpdatedFits = packetData->appendValue(encodedUpdateDelta); } if (successLastUpdatedFits) { propertyFlagsOffset = packetData->getUncompressedByteOffset(); encodedPropertyFlags = propertyFlags; oldPropertyFlagsLength = encodedPropertyFlags.length(); successPropertyFlagsFits = packetData->appendValue(encodedPropertyFlags); } bool headerFits = successIDFits && successTypeFits && successCreatedFits && successLastEditedFits && successLastUpdatedFits && successPropertyFlagsFits; int startOfEntityItemData = packetData->getUncompressedByteOffset(); if (headerFits) { bool successPropertyFits; propertyFlags -= PROP_LAST_ITEM; // clear the last item for now, we may or may not set it as the actual item // These items would go here once supported.... // PROP_PAGED_PROPERTY, // PROP_CUSTOM_PROPERTIES_INCLUDED, APPEND_ENTITY_PROPERTY(PROP_POSITION, appendPosition, getPosition()); APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, appendValue, getDimensions()); // NOTE: PROP_RADIUS obsolete if (wantDebug) { qDebug() << " APPEND_ENTITY_PROPERTY() PROP_DIMENSIONS:" << getDimensions(); } APPEND_ENTITY_PROPERTY(PROP_ROTATION, appendValue, getRotation()); APPEND_ENTITY_PROPERTY(PROP_MASS, appendValue, getMass()); APPEND_ENTITY_PROPERTY(PROP_VELOCITY, appendValue, getVelocity()); APPEND_ENTITY_PROPERTY(PROP_GRAVITY, appendValue, getGravity()); APPEND_ENTITY_PROPERTY(PROP_DAMPING, appendValue, getDamping()); APPEND_ENTITY_PROPERTY(PROP_LIFETIME, appendValue, getLifetime()); APPEND_ENTITY_PROPERTY(PROP_SCRIPT, appendValue, getScript()); APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, appendValue, getRegistrationPoint()); APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, appendValue, getAngularVelocity()); APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, appendValue, getAngularDamping()); APPEND_ENTITY_PROPERTY(PROP_VISIBLE, appendValue, getVisible()); APPEND_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, appendValue, getIgnoreForCollisions()); APPEND_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, appendValue, getCollisionsWillMove()); appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); } if (propertyCount > 0) { int endOfEntityItemData = packetData->getUncompressedByteOffset(); encodedPropertyFlags = propertyFlags; int newPropertyFlagsLength = encodedPropertyFlags.length(); packetData->updatePriorBytes(propertyFlagsOffset, (const unsigned char*)encodedPropertyFlags.constData(), encodedPropertyFlags.length()); // if the size of the PropertyFlags shrunk, we need to shift everything down to front of packet. if (newPropertyFlagsLength < oldPropertyFlagsLength) { int oldSize = packetData->getUncompressedSize(); const unsigned char* modelItemData = packetData->getUncompressedData(propertyFlagsOffset + oldPropertyFlagsLength); int modelItemDataLength = endOfEntityItemData - startOfEntityItemData; int newEntityItemDataStart = propertyFlagsOffset + newPropertyFlagsLength; packetData->updatePriorBytes(newEntityItemDataStart, modelItemData, modelItemDataLength); int newSize = oldSize - (oldPropertyFlagsLength - newPropertyFlagsLength); packetData->setUncompressedSize(newSize); } else { assert(newPropertyFlagsLength == oldPropertyFlagsLength); // should not have grown } packetData->endLevel(entityLevel); } else { packetData->discardLevel(entityLevel); appendState = OctreeElement::NONE; // if we got here, then we didn't include the item } // If any part of the model items didn't fit, then the element is considered partial if (appendState != OctreeElement::COMPLETED) { // add this item into our list for the next appendElementData() pass entityTreeElementExtraEncodeData->entities.insert(getEntityItemID(), propertiesDidntFit); } return appendState; }
bool WebEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking) const { glm::vec3 dimensions = getScaledDimensions(); glm::vec2 xyDimensions(dimensions.x, dimensions.y); glm::quat rotation = getWorldOrientation(); glm::vec3 position = getWorldPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); glm::quat inverseRot = glm::inverse(rotation); glm::vec3 localOrigin = inverseRot * (origin - position); glm::vec3 localVelocity = inverseRot * velocity; glm::vec3 localAcceleration = inverseRot * acceleration; if (findParabolaRectangleIntersection(localOrigin, localVelocity, localAcceleration, xyDimensions, parabolicDistance)) { float localIntersectionVelocityZ = localVelocity.z + localAcceleration.z * parabolicDistance; glm::vec3 forward = rotation * Vectors::FRONT; if (localIntersectionVelocityZ > 0.0f) { face = MIN_Z_FACE; surfaceNormal = forward; } else { face = MAX_Z_FACE; surfaceNormal = -forward; } return true; } else { return false; } }
bool WebEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking) const { glm::vec3 dimensions = getScaledDimensions(); glm::vec2 xyDimensions(dimensions.x, dimensions.y); glm::quat rotation = getWorldOrientation(); glm::vec3 position = getWorldPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); rotation = EntityItem::getBillboardRotation(position, rotation, _billboardMode, EntityItem::getPrimaryViewFrustumPosition()); if (findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance)) { glm::vec3 forward = rotation * Vectors::FRONT; if (glm::dot(forward, direction) > 0.0f) { face = MAX_Z_FACE; surfaceNormal = -forward; } else { face = MIN_Z_FACE; surfaceNormal = forward; } return true; } else { return false; } }
// NOTE: this only renders the "meta" portion of the Model, namely it renders debugging items, and it handles // the per frame simulation/update that might be required if the models properties changed. void RenderableModelEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RMEIrender"); assert(getType() == EntityTypes::Model); if (hasModel()) { if (_model) { if (getModelURL() != _model->getURL().toString()) { qDebug() << "Updating model URL: " << getModelURL(); _model->setURL(getModelURL()); } render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); // check to see if when we added our models to the scene they were ready, if they were not ready, then // fix them up in the scene if (_model->needsFixupInScene()) { render::PendingChanges pendingChanges; _model->removeFromScene(scene, pendingChanges); render::Item::Status::Getters statusGetters; makeEntityItemStatusGetters(this, statusGetters); _model->addToScene(scene, pendingChanges, statusGetters); scene->enqueuePendingChanges(pendingChanges); } // FIXME: this seems like it could be optimized if we tracked our last known visible state in // the renderable item. As it stands now the model checks it's visible/invisible state // so most of the time we don't do anything in this function. _model->setVisibleInScene(getVisible(), scene); } remapTextures(); { // float alpha = getLocalRenderAlpha(); if (!_model || _needsModelReload) { // TODO: this getModel() appears to be about 3% of model render time. We should optimize PerformanceTimer perfTimer("getModel"); EntityTreeRenderer* renderer = static_cast<EntityTreeRenderer*>(args->_renderer); getModel(renderer); } if (_model) { // handle animations.. if (hasAnimation()) { if (!jointsMapped()) { QStringList modelJointNames = _model->getJointNames(); mapJoints(modelJointNames); } if (jointsMapped()) { bool newFrame; QVector<glm::quat> frameDataRotations; QVector<glm::vec3> frameDataTranslations; getAnimationFrame(newFrame, frameDataRotations, frameDataTranslations); assert(frameDataRotations.size() == frameDataTranslations.size()); if (newFrame) { for (int i = 0; i < frameDataRotations.size(); i++) { _model->setJointState(i, true, frameDataRotations[i], frameDataTranslations[i], 1.0f); } } } } bool movingOrAnimating = isMoving() || isAnimatingSomething(); if ((movingOrAnimating || _needsInitialSimulation) && _model->isActive() && _dimensionsInitialized) { _model->setScaleToFit(true, getDimensions()); _model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); _model->setRotation(getRotation()); _model->setTranslation(getPosition()); // make sure to simulate so everything gets set up correctly for rendering { PerformanceTimer perfTimer("_model->simulate"); _model->simulate(0.0f); } _needsInitialSimulation = false; } } } } else { glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f); RenderableDebugableEntityItem::renderBoundingBox(this, args, 0.0f, greenColor); } RenderableDebugableEntityItem::render(this, args); }
// NOTE: this only renders the "meta" portion of the Model, namely it renders debugging items, and it handles // the per frame simulation/update that might be required if the models properties changed. void RenderableModelEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RMEIrender"); assert(getType() == EntityTypes::Model); if (hasModel()) { if (_model) { // check if the URL has changed auto& currentURL = getParsedModelURL(); if (currentURL != _model->getURL()) { qCDebug(entitiesrenderer).noquote() << "Updating model URL: " << currentURL.toDisplayString(); _model->setURL(currentURL); } render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); // check to see if when we added our models to the scene they were ready, if they were not ready, then // fix them up in the scene bool shouldShowCollisionHull = (args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS) > 0; if (_model->needsFixupInScene() || _showCollisionHull != shouldShowCollisionHull) { _showCollisionHull = shouldShowCollisionHull; render::PendingChanges pendingChanges; _model->removeFromScene(scene, pendingChanges); render::Item::Status::Getters statusGetters; makeEntityItemStatusGetters(getThisPointer(), statusGetters); _model->addToScene(scene, pendingChanges, statusGetters, _showCollisionHull); scene->enqueuePendingChanges(pendingChanges); } // FIXME: this seems like it could be optimized if we tracked our last known visible state in // the renderable item. As it stands now the model checks it's visible/invisible state // so most of the time we don't do anything in this function. _model->setVisibleInScene(getVisible(), scene); } remapTextures(); { // float alpha = getLocalRenderAlpha(); if (!_model || _needsModelReload) { // TODO: this getModel() appears to be about 3% of model render time. We should optimize PerformanceTimer perfTimer("getModel"); EntityTreeRenderer* renderer = static_cast<EntityTreeRenderer*>(args->_renderer); getModel(renderer); } if (_model) { if (hasAnimation()) { if (!jointsMapped()) { QStringList modelJointNames = _model->getJointNames(); mapJoints(modelJointNames); } } _jointDataLock.withWriteLock([&] { getAnimationFrame(); // relay any inbound joint changes from scripts/animation/network to the model/rig for (int index = 0; index < _absoluteJointRotationsInObjectFrame.size(); index++) { if (_absoluteJointRotationsInObjectFrameDirty[index]) { glm::quat rotation = _absoluteJointRotationsInObjectFrame[index]; _model->setJointRotation(index, true, rotation, 1.0f); _absoluteJointRotationsInObjectFrameDirty[index] = false; } } for (int index = 0; index < _absoluteJointTranslationsInObjectFrame.size(); index++) { if (_absoluteJointTranslationsInObjectFrameDirty[index]) { glm::vec3 translation = _absoluteJointTranslationsInObjectFrame[index]; _model->setJointTranslation(index, true, translation, 1.0f); _absoluteJointTranslationsInObjectFrameDirty[index] = false; } } }); bool movingOrAnimating = isMoving() || isAnimatingSomething(); if ((movingOrAnimating || _needsInitialSimulation || _model->getTranslation() != getPosition() || _model->getRotation() != getRotation() || _model->getRegistrationPoint() != getRegistrationPoint()) && _model->isActive() && _dimensionsInitialized) { _model->setScaleToFit(true, getDimensions()); _model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); _model->setRotation(getRotation()); _model->setTranslation(getPosition()); // make sure to simulate so everything gets set up correctly for rendering { PerformanceTimer perfTimer("_model->simulate"); _model->simulate(0.0f); } _needsInitialSimulation = false; } } } } else { static glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f); gpu::Batch& batch = *args->_batch; bool success; auto shapeTransform = getTransformToCenter(success); if (success) { batch.setModelTransform(Transform()); // we want to include the scale as well DependencyManager::get<GeometryCache>()->renderWireCubeInstance(batch, shapeTransform, greenColor); } } }
void RenderableModelEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RMEIrender"); assert(getType() == EntityTypes::Model); bool drawAsModel = hasModel(); glm::vec3 position = getPosition() * (float)TREE_SCALE; float size = getSize() * (float)TREE_SCALE; glm::vec3 dimensions = getDimensions() * (float)TREE_SCALE; if (drawAsModel) { glPushMatrix(); { float alpha = getLocalRenderAlpha(); if (!_model || _needsModelReload) { // TODO: this getModel() appears to be about 3% of model render time. We should optimize PerformanceTimer perfTimer("getModel"); EntityTreeRenderer* renderer = static_cast<EntityTreeRenderer*>(args->_renderer); getModel(renderer); } if (_model) { // handle animations.. if (hasAnimation()) { if (!jointsMapped()) { QStringList modelJointNames = _model->getJointNames(); mapJoints(modelJointNames); } if (jointsMapped()) { QVector<glm::quat> frameData = getAnimationFrame(); for (int i = 0; i < frameData.size(); i++) { _model->setJointState(i, true, frameData[i]); } } } glm::quat rotation = getRotation(); if (needsSimulation() && _model->isActive()) { _model->setScaleToFit(true, dimensions); _model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); _model->setRotation(rotation); _model->setTranslation(position); // make sure to simulate so everything gets set up correctly for rendering { PerformanceTimer perfTimer("_model->simulate"); _model->simulate(0.0f); } _needsInitialSimulation = false; } // TODO: should we allow entityItems to have alpha on their models? Model::RenderMode modelRenderMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE ? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE; if (_model->isActive()) { // TODO: this is the majority of model render time. And rendering of a cube model vs the basic Box render // is significantly more expensive. Is there a way to call this that doesn't cost us as much? PerformanceTimer perfTimer("model->render"); _model->render(alpha, modelRenderMode, args); } else { // if we couldn't get a model, then just draw a cube glColor3ub(getColor()[RED_INDEX],getColor()[GREEN_INDEX],getColor()[BLUE_INDEX]); glPushMatrix(); glTranslatef(position.x, position.y, position.z); Application::getInstance()->getDeferredLightingEffect()->renderWireCube(size); glPopMatrix(); } } else { // if we couldn't get a model, then just draw a cube glColor3ub(getColor()[RED_INDEX],getColor()[GREEN_INDEX],getColor()[BLUE_INDEX]); glPushMatrix(); glTranslatef(position.x, position.y, position.z); Application::getInstance()->getDeferredLightingEffect()->renderWireCube(size); glPopMatrix(); } } glPopMatrix(); } else { glColor3ub(getColor()[RED_INDEX],getColor()[GREEN_INDEX],getColor()[BLUE_INDEX]); glPushMatrix(); glTranslatef(position.x, position.y, position.z); Application::getInstance()->getDeferredLightingEffect()->renderWireCube(size); glPopMatrix(); } }
void RenderableModelEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RMEIrender"); assert(getType() == EntityTypes::Model); bool drawAsModel = hasModel(); glm::vec3 position = getPosition(); glm::vec3 dimensions = getDimensions(); float size = glm::length(dimensions); bool highlightSimulationOwnership = false; if (args->_debugFlags & RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP) { auto nodeList = DependencyManager::get<NodeList>(); const QUuid& myNodeID = nodeList->getSessionUUID(); highlightSimulationOwnership = (getSimulatorID() == myNodeID); } if (drawAsModel && !highlightSimulationOwnership) { remapTextures(); glPushMatrix(); { float alpha = getLocalRenderAlpha(); if (!_model || _needsModelReload) { // TODO: this getModel() appears to be about 3% of model render time. We should optimize PerformanceTimer perfTimer("getModel"); EntityTreeRenderer* renderer = static_cast<EntityTreeRenderer*>(args->_renderer); getModel(renderer); } if (_model) { // handle animations.. if (hasAnimation()) { if (!jointsMapped()) { QStringList modelJointNames = _model->getJointNames(); mapJoints(modelJointNames); } if (jointsMapped()) { QVector<glm::quat> frameData = getAnimationFrame(); for (int i = 0; i < frameData.size(); i++) { _model->setJointState(i, true, frameData[i]); } } } glm::quat rotation = getRotation(); bool movingOrAnimating = isMoving() || isAnimatingSomething(); if ((movingOrAnimating || _needsInitialSimulation) && _model->isActive()) { _model->setScaleToFit(true, dimensions); _model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); _model->setRotation(rotation); _model->setTranslation(position); // make sure to simulate so everything gets set up correctly for rendering { PerformanceTimer perfTimer("_model->simulate"); _model->simulate(0.0f); } _needsInitialSimulation = false; } if (_model->isActive()) { // TODO: this is the majority of model render time. And rendering of a cube model vs the basic Box render // is significantly more expensive. Is there a way to call this that doesn't cost us as much? PerformanceTimer perfTimer("model->render"); // filter out if not needed to render if (args && (args->_renderMode == RenderArgs::SHADOW_RENDER_MODE)) { if (movingOrAnimating) { _model->renderInScene(alpha, args); } } else { _model->renderInScene(alpha, args); } } else { // if we couldn't get a model, then just draw a cube glm::vec4 color(getColor()[RED_INDEX]/255, getColor()[GREEN_INDEX]/255, getColor()[BLUE_INDEX]/255, 1.0f); glPushMatrix(); glTranslatef(position.x, position.y, position.z); DependencyManager::get<DeferredLightingEffect>()->renderWireCube(size, color); glPopMatrix(); } } else { // if we couldn't get a model, then just draw a cube glm::vec4 color(getColor()[RED_INDEX]/255, getColor()[GREEN_INDEX]/255, getColor()[BLUE_INDEX]/255, 1.0f); glPushMatrix(); glTranslatef(position.x, position.y, position.z); DependencyManager::get<DeferredLightingEffect>()->renderWireCube(size, color); glPopMatrix(); } } glPopMatrix(); } else { glm::vec4 color(getColor()[RED_INDEX]/255, getColor()[GREEN_INDEX]/255, getColor()[BLUE_INDEX]/255, 1.0f); glPushMatrix(); glTranslatef(position.x, position.y, position.z); DependencyManager::get<DeferredLightingEffect>()->renderWireCube(size, color); glPopMatrix(); } }