Merge pull request #3690 from ZappoMan/rayPickSubMeshes

Ray pick sub meshes
This commit is contained in:
Stephen Birarda 2014-10-29 12:10:29 -07:00
commit 5b3897f1cf
41 changed files with 432 additions and 147 deletions

View file

@ -0,0 +1,87 @@
var position = Vec3.sum(MyAvatar.position, { x: 0, y: -1, z: 0});
var scalingFactor = 0.01;
var sphereNaturalExtentsMin = { x: -1230, y: -1223, z: -1210 };
var sphereNaturalExtentsMax = { x: 1230, y: 1229, z: 1223 };
var panelsNaturalExtentsMin = { x: -1181, y: -326, z: 56 };
var panelsNaturalExtentsMax = { x: 1181, y: 576, z: 1183 };
var sphereNaturalDimensions = Vec3.subtract(sphereNaturalExtentsMax, sphereNaturalExtentsMin);
var panelsNaturalDimensions = Vec3.subtract(panelsNaturalExtentsMax, panelsNaturalExtentsMin);
Vec3.print("sphereNaturalDimensions:", sphereNaturalDimensions);
Vec3.print("panelsNaturalDimensions:", panelsNaturalDimensions);
var sphereNaturalCenter = Vec3.sum(sphereNaturalExtentsMin, Vec3.multiply(sphereNaturalDimensions, 0.5));
var panelsNaturalCenter = Vec3.sum(panelsNaturalExtentsMin, Vec3.multiply(panelsNaturalDimensions, 0.5));
Vec3.print("sphereNaturalCenter:", sphereNaturalCenter);
Vec3.print("panelsNaturalCenter:", panelsNaturalCenter);
var sphereDimensions = Vec3.multiply(sphereNaturalDimensions, scalingFactor);
var panelsDimensions = Vec3.multiply(panelsNaturalDimensions, scalingFactor);
Vec3.print("sphereDimensions:", sphereDimensions);
Vec3.print("panelsDimensions:", panelsDimensions);
var sphereCenter = Vec3.multiply(sphereNaturalCenter, scalingFactor);
var panelsCenter = Vec3.multiply(panelsNaturalCenter, scalingFactor);
Vec3.print("sphereCenter:", sphereCenter);
Vec3.print("panelsCenter:", panelsCenter);
var centerShift = Vec3.subtract(panelsCenter, sphereCenter);
Vec3.print("centerShift:", centerShift);
var spherePosition = position;
Vec3.print("spherePosition:", spherePosition);
var panelsPosition = Vec3.sum(spherePosition, centerShift);
Vec3.print("panelsPosition:", panelsPosition);
var screensOverlay = Overlays.addOverlay("model", {
position: panelsPosition,
dimensions: panelsDimensions,
url: "https://s3.amazonaws.com/hifi-public/models/sets/Lobby/LobbyConcepts/Lobby5_IsolatedPanelsFreezeTransforms.fbx"
});
var structureOverlay = Overlays.addOverlay("model", {
position: spherePosition,
dimensions: sphereDimensions,
url: "https://s3.amazonaws.com/hifi-public/models/sets/Lobby/LobbyConcepts/Lobby5_OrbShellOnly.fbx",
ignoreRayIntersection: true, // we don't want to ray pick against any of this
});
var statusText = Overlays.addOverlay("text", {
x: 200,
y: 100,
width: 200,
height: 20,
backgroundColor: { red: 0, green: 0, blue: 0},
alpha: 1.0,
color: { red: 255, green: 255, blue: 255},
topMargin: 4,
leftMargin: 4,
text: "",
});
Controller.mouseMoveEvent.connect(function(event){
var pickRay = Camera.computePickRay(event.x, event.y);
var result = Overlays.findRayIntersection(pickRay);
if (result.intersects) {
if (result.overlayID == screensOverlay) {
Overlays.editOverlay(statusText, { text: "You are pointing at: " + result.extraInfo });
} else {
Overlays.editOverlay(statusText, { text: "You are not pointing at a panel..." });
}
} else {
Overlays.editOverlay(statusText, { text: "You are not pointing at a panel..." });
}
});
Script.scriptEnding.connect(function(){
Overlays.deleteOverlay(screensOverlay);
Overlays.deleteOverlay(structureOverlay);
Overlays.deleteOverlay(statusText);
});

View file

@ -2787,7 +2787,7 @@ void Application::updateShadowMap() {
{
PerformanceTimer perfTimer("entities");
_entities.render(OctreeRenderer::SHADOW_RENDER_MODE);
_entities.render(RenderArgs::SHADOW_RENDER_MODE);
}
glDisable(GL_POLYGON_OFFSET_FILL);

View file

@ -18,6 +18,7 @@
#include <BoxEntityItem.h>
#include <ModelEntityItem.h>
#include <PerfStat.h>
#include <RenderArgs.h>
#include "Menu.h"
@ -67,7 +68,7 @@ void EntityTreeRenderer::update() {
}
}
void EntityTreeRenderer::render(RenderMode renderMode) {
void EntityTreeRenderer::render(RenderArgs::RenderMode renderMode) {
OctreeRenderer::render(renderMode);
deleteReleasedModels(); // seems like as good as any other place to do some memory cleanup
}
@ -165,7 +166,7 @@ void renderElementProxy(EntityTreeElement* entityTreeElement) {
}
void EntityTreeRenderer::renderProxies(const EntityItem* entity, RenderArgs* args) {
bool isShadowMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE;
bool isShadowMode = args->_renderMode == RenderArgs::SHADOW_RENDER_MODE;
bool displayModelBounds = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelBounds);
if (!isShadowMode && displayModelBounds) {
PerformanceTimer perfTimer("renderProxies");
@ -239,7 +240,7 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
uint16_t numberOfEntities = entityItems.size();
bool isShadowMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE;
bool isShadowMode = args->_renderMode == RenderArgs::SHADOW_RENDER_MODE;
bool displayElementProxy = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelElementProxy);

View file

@ -20,6 +20,7 @@
#include <OctreePacketData.h>
#include <OctreeRenderer.h>
#include <PacketHeaders.h>
#include <RenderArgs.h>
#include <SharedUtil.h>
#include <ViewFrustum.h>
@ -48,7 +49,7 @@ public:
void processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode);
virtual void init();
virtual void render(RenderMode renderMode = DEFAULT_RENDER_MODE);
virtual void render(RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE);
virtual const FBXGeometry* getGeometryForEntity(const EntityItem* entityItem);
virtual const Model* getModelForEntityItem(const EntityItem* entityItem);

View file

@ -112,7 +112,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
}
// TODO: should we allow entityItems to have alpha on their models?
Model::RenderMode modelRenderMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE
Model::RenderMode modelRenderMode = args->_renderMode == RenderArgs::SHADOW_RENDER_MODE
? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
if (_model->isActive()) {

View file

@ -36,6 +36,7 @@ using namespace std;
static int modelPointerTypeId = qRegisterMetaType<QPointer<Model> >();
static int weakNetworkGeometryPointerTypeId = qRegisterMetaType<QWeakPointer<NetworkGeometry> >();
static int vec3VectorTypeId = qRegisterMetaType<QVector<glm::vec3> >();
float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f;
Model::Model(QObject* parent) :
QObject(parent),
@ -124,35 +125,64 @@ void Model::setOffset(const glm::vec3& offset) {
void Model::initProgram(ProgramObject& program, Model::Locations& locations, int specularTextureUnit) {
program.bind();
#ifdef Q_OS_MAC
// HACK: Assign explicitely the attribute channel to avoid a bug on Yosemite
glBindAttribLocation(program.programId(), 4, "tangent");
glLinkProgram(program.programId());
#endif
locations.tangent = program.attributeLocation("tangent");
locations.alphaThreshold = program.uniformLocation("alphaThreshold");
program.setUniformValue("diffuseMap", 0);
program.setUniformValue("normalMap", 1);
program.setUniformValue("specularMap", specularTextureUnit);
program.release();
}
void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locations, int specularTextureUnit) {
initProgram(program, locations, specularTextureUnit);
#ifdef Q_OS_MAC
// HACK: Assign explicitely the attribute channel to avoid a bug on Yosemite
glBindAttribLocation(program.programId(), 5, "clusterIndices");
glBindAttribLocation(program.programId(), 6, "clusterWeights");
glLinkProgram(program.programId());
#endif
program.bind();
locations.clusterMatrices = program.uniformLocation("clusterMatrices");
locations.clusterIndices = program.attributeLocation("clusterIndices");
locations.clusterWeights = program.attributeLocation("clusterWeights");
#ifdef Q_OS_MAC
// HACK: Assign explicitely the attribute channel to avoid a bug on Yosemite
glBindAttribLocation(program.programId(), 4, "tangent");
glLinkProgram(program.programId());
#endif
locations.tangent = program.attributeLocation("tangent");
locations.alphaThreshold = program.uniformLocation("alphaThreshold");
program.setUniformValue("diffuseMap", 0);
program.setUniformValue("normalMap", 1);
program.setUniformValue("specularMap", specularTextureUnit);
program.release();
}
void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locations, int specularTextureUnit) {
initProgram(program, locations, specularTextureUnit);
#ifdef Q_OS_MAC
// HACK: Assign explicitely the attribute channel to avoid a bug on Yosemite
glBindAttribLocation(program.programId(), 5, "clusterIndices");
glBindAttribLocation(program.programId(), 6, "clusterWeights");
glLinkProgram(program.programId());
#endif
program.bind();
locations.clusterMatrices = program.uniformLocation("clusterMatrices");
locations.clusterIndices = program.attributeLocation("clusterIndices");
locations.clusterWeights = program.attributeLocation("clusterWeights");
program.release();
}
@ -410,6 +440,80 @@ void Model::setJointStates(QVector<JointState> states) {
_boundingRadius = radius;
}
bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction,
float& distance, BoxFace& face, QString& extraInfo) const {
bool intersectedSomething = false;
// if we aren't active, we can't ray pick yet...
if (!isActive()) {
return intersectedSomething;
}
// extents is the entity relative, scaled, centered extents of the entity
glm::vec3 position = _translation;
glm::mat4 rotation = glm::mat4_cast(_rotation);
glm::mat4 translation = glm::translate(position);
glm::mat4 modelToWorldMatrix = translation * rotation;
glm::mat4 worldToModelMatrix = glm::inverse(modelToWorldMatrix);
Extents modelExtents = getMeshExtents(); // NOTE: unrotated
glm::vec3 dimensions = modelExtents.maximum - modelExtents.minimum;
glm::vec3 corner = dimensions * -0.5f; // since we're going to do the ray picking in the model frame of reference
AABox overlayFrameBox(corner, dimensions);
glm::vec3 modelFrameOrigin = glm::vec3(worldToModelMatrix * glm::vec4(origin, 1.0f));
glm::vec3 modelFrameDirection = glm::vec3(worldToModelMatrix * glm::vec4(direction, 0.0f));
// we can use the AABox's ray intersection by mapping our origin and direction into the model frame
// and testing intersection there.
if (overlayFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, distance, face)) {
float bestDistance = std::numeric_limits<float>::max();
float distanceToSubMesh;
BoxFace subMeshFace;
BoxFace bestSubMeshFace;
int subMeshIndex = 0;
int bestSubMeshIndex = -1;
// If we hit the models box, then consider the submeshes...
foreach(const AABox& subMeshBox, _calculatedMeshBoxes) {
const FBXGeometry& geometry = _geometry->getFBXGeometry();
if (subMeshBox.findRayIntersection(origin, direction, distanceToSubMesh, subMeshFace)) {
if (distanceToSubMesh < bestDistance) {
bestSubMeshIndex = subMeshIndex;
bestDistance = distanceToSubMesh;
bestSubMeshFace = subMeshFace;
intersectedSomething = true;
extraInfo = geometry.getModelNameOfMesh(subMeshIndex);
}
}
subMeshIndex++;
}
return intersectedSomething;
}
return intersectedSomething;
}
void Model::recalcuateMeshBoxes() {
if (!_calculatedMeshBoxesValid) {
PerformanceTimer perfTimer("calculatedMeshBoxes");
const FBXGeometry& geometry = _geometry->getFBXGeometry();
int numberOfMeshes = geometry.meshes.size();
_calculatedMeshBoxes.resize(numberOfMeshes);
for (int i = 0; i < numberOfMeshes; i++) {
const FBXMesh& mesh = geometry.meshes.at(i);
Extents scaledMeshExtents = calculateScaledOffsetExtents(mesh.meshExtents);
_calculatedMeshBoxes[i] = AABox(scaledMeshExtents);
}
_calculatedMeshBoxesValid = true;
}
}
bool Model::render(float alpha, RenderMode mode, RenderArgs* args) {
PROFILE_RANGE(__FUNCTION__);
// render the attachments
@ -425,16 +529,7 @@ bool Model::render(float alpha, RenderMode mode, RenderArgs* args) {
// against. We cache the results of these calculations so long as the model hasn't been
// simulated and the mesh hasn't changed.
if (args && !_calculatedMeshBoxesValid) {
PerformanceTimer perfTimer("calculatedMeshBoxes");
const FBXGeometry& geometry = _geometry->getFBXGeometry();
int numberOfMeshes = geometry.meshes.size();
_calculatedMeshBoxes.resize(numberOfMeshes);
for (int i = 0; i < numberOfMeshes; i++) {
const FBXMesh& mesh = geometry.meshes.at(i);
Extents scaledMeshExtents = calculateScaledOffsetExtents(mesh.meshExtents);
_calculatedMeshBoxes[i] = AABox(scaledMeshExtents);
}
_calculatedMeshBoxesValid = true;
recalcuateMeshBoxes();
}
// set up dilated textures on first render after load/simulate
@ -874,7 +969,15 @@ void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions) {
}
void Model::setScaleToFit(bool scaleToFit, float largestDimension) {
// NOTE: if the model is not active, then it means we don't actually know the true/natural dimensions of the
// mesh, and so we can't do the needed calculations for scaling to fit to a single largest dimension. In this
// case we will record that we do want to do this, but we will stick our desired single dimension into the
// first element of the vec3 for the non-fixed aspect ration dimensions
if (!isActive()) {
_scaleToFit = scaleToFit;
if (scaleToFit) {
_scaleToFitDimensions = glm::vec3(largestDimension, FAKE_DIMENSION_PLACEHOLDER, FAKE_DIMENSION_PLACEHOLDER);
}
return;
}
@ -896,6 +999,15 @@ void Model::setScaleToFit(bool scaleToFit, float largestDimension) {
}
void Model::scaleToFit() {
// If our _scaleToFitDimensions.y/z are FAKE_DIMENSION_PLACEHOLDER then it means our
// user asked to scale us in a fixed aspect ratio to a single largest dimension, but
// we didn't yet have an active mesh. We can only enter this scaleToFit() in this state
// if we now do have an active mesh, so we take this opportunity to actually determine
// the correct scale.
if (_scaleToFit && _scaleToFitDimensions.y == FAKE_DIMENSION_PLACEHOLDER
&& _scaleToFitDimensions.z == FAKE_DIMENSION_PLACEHOLDER) {
setScaleToFit(_scaleToFit, _scaleToFitDimensions.x);
}
Extents modelMeshExtents = getUnscaledMeshExtents();
// size is our "target size in world space"

View file

@ -189,13 +189,18 @@ public:
Q_INVOKABLE void setTextureWithNameToURL(const QString& name, const QUrl& url)
{ _geometry->setTextureWithNameToURL(name, url); }
bool findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction,
float& distance, BoxFace& face, QString& extraInfo) const;
protected:
QSharedPointer<NetworkGeometry> _geometry;
glm::vec3 _scale;
glm::vec3 _offset;
static float FAKE_DIMENSION_PLACEHOLDER;
bool _scaleToFit; /// If you set scaleToFit, we will calculate scale based on MeshExtents
glm::vec3 _scaleToFitDimensions; /// this is the dimensions that scale to fit will use
bool _scaledToFit; /// have we scaled to fit
@ -341,6 +346,8 @@ private:
QVector<AABox> _calculatedMeshBoxes;
bool _calculatedMeshBoxesValid;
void recalcuateMeshBoxes();
void segregateMeshGroups(); // used to calculate our list of translucent vs opaque meshes
bool _meshGroupsKnown;

View file

@ -48,6 +48,11 @@ public:
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const;
virtual bool findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction,
float& distance, BoxFace& face, QString& extraInfo) const {
return findRayIntersection(origin, direction, distance, face);
}
protected:
void drawDashedLine(const glm::vec3& start, const glm::vec3& end);

View file

@ -24,7 +24,7 @@ BillboardOverlay::BillboardOverlay() :
_isLoaded = false;
}
void BillboardOverlay::render() {
void BillboardOverlay::render(RenderArgs* args) {
if (!_visible || !_isLoaded) {
return;
}

View file

@ -23,7 +23,7 @@ class BillboardOverlay : public Base3DOverlay {
public:
BillboardOverlay();
virtual void render();
virtual void render(RenderArgs* args);
// setters
void setURL(const QString& url);

View file

@ -36,7 +36,7 @@ Circle3DOverlay::Circle3DOverlay() :
Circle3DOverlay::~Circle3DOverlay() {
}
void Circle3DOverlay::render() {
void Circle3DOverlay::render(RenderArgs* args) {
if (!_visible) {
return; // do nothing if we're not visible
}

View file

@ -19,7 +19,7 @@ class Circle3DOverlay : public Planar3DOverlay {
public:
Circle3DOverlay();
~Circle3DOverlay();
virtual void render();
virtual void render(RenderArgs* args);
virtual void setProperties(const QScriptValue& properties);
float getStartAt() const { return _startAt; }

View file

@ -25,7 +25,7 @@ Cube3DOverlay::Cube3DOverlay() {
Cube3DOverlay::~Cube3DOverlay() {
}
void Cube3DOverlay::render() {
void Cube3DOverlay::render(RenderArgs* args) {
if (!_visible) {
return; // do nothing if we're not visible
}

View file

@ -19,7 +19,7 @@ class Cube3DOverlay : public Volume3DOverlay {
public:
Cube3DOverlay();
~Cube3DOverlay();
virtual void render();
virtual void render(RenderArgs* args);
};

View file

@ -52,7 +52,7 @@ void ImageOverlay::replyFinished() {
_isLoaded = true;
}
void ImageOverlay::render() {
void ImageOverlay::render(RenderArgs* args) {
if (!_visible || !_isLoaded) {
return; // do nothing if we're not visible
}

View file

@ -34,7 +34,7 @@ class ImageOverlay : public Overlay2D {
public:
ImageOverlay();
~ImageOverlay();
virtual void render();
virtual void render(RenderArgs* args);
// getters
const QRect& getClipFromSource() const { return _fromImage; }

View file

@ -21,7 +21,7 @@ Line3DOverlay::Line3DOverlay() {
Line3DOverlay::~Line3DOverlay() {
}
void Line3DOverlay::render() {
void Line3DOverlay::render(RenderArgs* args) {
if (!_visible) {
return; // do nothing if we're not visible
}

View file

@ -19,7 +19,7 @@ class Line3DOverlay : public Base3DOverlay {
public:
Line3DOverlay();
~Line3DOverlay();
virtual void render();
virtual void render(RenderArgs* args);
// getters
const glm::vec3& getEnd() const { return _end; }

View file

@ -25,7 +25,7 @@ void LocalModelsOverlay::update(float deltatime) {
_entityTreeRenderer->update();
}
void LocalModelsOverlay::render() {
void LocalModelsOverlay::render(RenderArgs* args) {
if (_visible) {
float glowLevel = getGlowLevel();

View file

@ -23,7 +23,7 @@ public:
~LocalModelsOverlay();
virtual void update(float deltatime);
virtual void render();
virtual void render(RenderArgs* args);
private:
EntityTreeRenderer* _entityTreeRenderer;

View file

@ -51,7 +51,7 @@ void LocalVoxelsOverlay::update(float deltatime) {
_tree->unlock();
}
void LocalVoxelsOverlay::render() {
void LocalVoxelsOverlay::render(RenderArgs* args) {
glm::vec3 dimensions = getDimensions();
float size = glm::length(dimensions);
if (_visible && size > 0 && _voxelSystem && _voxelSystem->isInitialized()) {

View file

@ -35,7 +35,7 @@ public:
~LocalVoxelsOverlay();
virtual void update(float deltatime);
virtual void render();
virtual void render(RenderArgs* args);
virtual void setProperties(const QScriptValue& properties);

View file

@ -25,7 +25,6 @@ void ModelOverlay::update(float deltatime) {
if (_updateModel) {
_updateModel = false;
_model.setScaleToFit(true, _scale);
_model.setSnapModelToCenter(true);
_model.setRotation(_rotation);
_model.setTranslation(_position);
@ -37,7 +36,7 @@ void ModelOverlay::update(float deltatime) {
_isLoaded = _model.isActive();
}
void ModelOverlay::render() {
void ModelOverlay::render(RenderArgs* args) {
if (!_visible) {
return;
}
@ -49,7 +48,7 @@ void ModelOverlay::render() {
if (glowLevel > 0.0f) {
glower = new Glower(glowLevel);
}
_model.render(getAlpha());
_model.render(getAlpha(), Model::DEFAULT_RENDER_MODE, args);
if (glower) {
delete glower;
}
@ -70,6 +69,7 @@ void ModelOverlay::setProperties(const QScriptValue &properties) {
QScriptValue scaleValue = properties.property("scale");
if (scaleValue.isValid()) {
_scale = scaleValue.toVariant().toFloat();
_model.setScaleToFit(true, _scale);
_updateModel = true;
}
@ -124,33 +124,15 @@ void ModelOverlay::setProperties(const QScriptValue &properties) {
bool ModelOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
float& distance, BoxFace& face) const {
// if our model isn't active, we can't ray pick yet...
if (!_model.isActive()) {
return false;
}
// extents is the entity relative, scaled, centered extents of the entity
glm::vec3 position = getPosition();
glm::mat4 rotation = glm::mat4_cast(getRotation());
glm::mat4 translation = glm::translate(position);
glm::mat4 entityToWorldMatrix = translation * rotation;
glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
Extents modelExtents = _model.getMeshExtents(); // NOTE: unrotated
glm::vec3 dimensions = modelExtents.maximum - modelExtents.minimum;
glm::vec3 corner = dimensions * -0.5f; // since we're going to do the ray picking in the overlay frame of reference
AABox overlayFrameBox(corner, dimensions);
glm::vec3 overlayFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f));
glm::vec3 overlayFrameDirection = glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f));
// we can use the AABox's ray intersection by mapping our origin and direction into the overlays frame
// and testing intersection there.
if (overlayFrameBox.findRayIntersection(overlayFrameOrigin, overlayFrameDirection, distance, face)) {
return true;
}
return false;
QString subMeshNameTemp;
return _model.findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, subMeshNameTemp);
}
bool ModelOverlay::findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction,
float& distance, BoxFace& face, QString& extraInfo) const {
return _model.findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, extraInfo);
}

View file

@ -22,9 +22,11 @@ public:
ModelOverlay();
virtual void update(float deltatime);
virtual void render();
virtual void render(RenderArgs* args);
virtual void setProperties(const QScriptValue& properties);
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const;
virtual bool findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction,
float& distance, BoxFace& face, QString& extraInfo) const;
private:

View file

@ -20,6 +20,7 @@
#include <QString>
#include <SharedUtil.h> // for xColor
#include <RenderArgs.h>
const xColor DEFAULT_OVERLAY_COLOR = { 255, 255, 255 };
const float DEFAULT_ALPHA = 0.7f;
@ -37,7 +38,7 @@ public:
~Overlay();
void init(QGLWidget* parent);
virtual void update(float deltatime) {}
virtual void render() = 0;
virtual void render(RenderArgs* args) = 0;
// getters
virtual bool is3D() const = 0;

View file

@ -10,6 +10,7 @@
#include <limits>
#include <Application.h>
#include <Menu.h>
#include "BillboardOverlay.h"
#include "Circle3DOverlay.h"
@ -78,8 +79,13 @@ void Overlays::update(float deltatime) {
void Overlays::render2D() {
QReadLocker lock(&_lock);
RenderArgs args = { NULL, Application::getInstance()->getViewFrustum(),
Menu::getInstance()->getVoxelSizeScale(), Menu::getInstance()->getBoundaryLevelAdjust(),
RenderArgs::DEFAULT_RENDER_MODE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
foreach(Overlay* thisOverlay, _overlays2D) {
thisOverlay->render();
thisOverlay->render(&args);
}
}
@ -95,6 +101,11 @@ void Overlays::render3D() {
float angle = 0.0f;
glm::vec3 axis(0.0f, 1.0f, 0.0f);
float myAvatarScale = 1.0f;
RenderArgs args = { NULL, Application::getInstance()->getViewFrustum(),
Menu::getInstance()->getVoxelSizeScale(), Menu::getInstance()->getBoundaryLevelAdjust(),
RenderArgs::DEFAULT_RENDER_MODE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
foreach(Overlay* thisOverlay, _overlays3D) {
glPushMatrix();
@ -118,7 +129,7 @@ void Overlays::render3D() {
default:
break;
}
thisOverlay->render();
thisOverlay->render(&args);
glPopMatrix();
}
}
@ -239,7 +250,8 @@ RayToOverlayIntersectionResult Overlays::findRayIntersection(const PickRay& ray)
if (thisOverlay->getVisible() && !thisOverlay->getIgnoreRayIntersection() && thisOverlay->isLoaded()) {
float thisDistance;
BoxFace thisFace;
if (thisOverlay->findRayIntersection(ray.origin, ray.direction, thisDistance, thisFace)) {
QString thisExtraInfo;
if (thisOverlay->findRayIntersectionExtraInfo(ray.origin, ray.direction, thisDistance, thisFace, thisExtraInfo)) {
if (thisDistance < bestDistance) {
bestDistance = thisDistance;
result.intersects = true;
@ -247,6 +259,7 @@ RayToOverlayIntersectionResult Overlays::findRayIntersection(const PickRay& ray)
result.face = thisFace;
result.overlayID = thisID;
result.intersection = ray.origin + (ray.direction * thisDistance);
result.extraInfo = thisExtraInfo;
}
}
}
@ -259,7 +272,8 @@ RayToOverlayIntersectionResult::RayToOverlayIntersectionResult() :
overlayID(-1),
distance(0),
face(),
intersection()
intersection(),
extraInfo()
{
}
@ -298,6 +312,7 @@ QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine,
obj.setProperty("face", faceName);
QScriptValue intersection = vec3toScriptValue(engine, value.intersection);
obj.setProperty("intersection", intersection);
obj.setProperty("extraInfo", value.extraInfo);
return obj;
}
@ -326,6 +341,7 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R
if (intersection.isValid()) {
vec3FromScriptValue(intersection, value.intersection);
}
value.extraInfo = object.property("extraInfo").toVariant().toString();
}
bool Overlays::isLoaded(unsigned int id) {

View file

@ -11,6 +11,7 @@
#ifndef hifi_Overlays_h
#define hifi_Overlays_h
#include <QString>
#include <QScriptValue>
#include "Overlay.h"
@ -23,6 +24,7 @@ public:
float distance;
BoxFace face;
glm::vec3 intersection;
QString extraInfo;
};
Q_DECLARE_METATYPE(RayToOverlayIntersectionResult);

View file

@ -23,7 +23,7 @@ Rectangle3DOverlay::Rectangle3DOverlay() {
Rectangle3DOverlay::~Rectangle3DOverlay() {
}
void Rectangle3DOverlay::render() {
void Rectangle3DOverlay::render(RenderArgs* args) {
if (!_visible) {
return; // do nothing if we're not visible
}

View file

@ -19,7 +19,7 @@ class Rectangle3DOverlay : public Planar3DOverlay {
public:
Rectangle3DOverlay();
~Rectangle3DOverlay();
virtual void render();
virtual void render(RenderArgs* args);
virtual void setProperties(const QScriptValue& properties);
};

View file

@ -24,7 +24,7 @@ Sphere3DOverlay::Sphere3DOverlay() {
Sphere3DOverlay::~Sphere3DOverlay() {
}
void Sphere3DOverlay::render() {
void Sphere3DOverlay::render(RenderArgs* args) {
if (!_visible) {
return; // do nothing if we're not visible
}

View file

@ -19,7 +19,7 @@ class Sphere3DOverlay : public Volume3DOverlay {
public:
Sphere3DOverlay();
~Sphere3DOverlay();
virtual void render();
virtual void render(RenderArgs* args);
};

View file

@ -52,7 +52,7 @@ xColor Text3DOverlay::getBackgroundColor() {
}
void Text3DOverlay::render() {
void Text3DOverlay::render(RenderArgs* args) {
if (!_visible) {
return; // do nothing if we're not visible
}

View file

@ -15,6 +15,8 @@
#include "InterfaceConfig.h"
#include <QString>
#include <RenderArgs.h>
#include "Planar3DOverlay.h"
class Text3DOverlay : public Planar3DOverlay {
@ -23,7 +25,7 @@ class Text3DOverlay : public Planar3DOverlay {
public:
Text3DOverlay();
~Text3DOverlay();
virtual void render();
virtual void render(RenderArgs* args);
// getters
const QString& getText() const { return _text; }

View file

@ -48,7 +48,7 @@ xColor TextOverlay::getBackgroundColor() {
}
void TextOverlay::render() {
void TextOverlay::render(RenderArgs* args) {
if (!_visible) {
return; // do nothing if we're not visible
}

View file

@ -37,7 +37,7 @@ class TextOverlay : public Overlay2D {
public:
TextOverlay();
~TextOverlay();
virtual void render();
virtual void render(RenderArgs* args);
// getters
const QString& getText() const { return _text; }

View file

@ -70,6 +70,14 @@ Extents FBXGeometry::getUnscaledMeshExtents() const {
return scaledExtents;
}
QString FBXGeometry::getModelNameOfMesh(int meshIndex) const {
if (meshIndicesToModelNames.contains(meshIndex)) {
return meshIndicesToModelNames.value(meshIndex);
}
return QString();
}
static int fbxGeometryMetaTypeId = qRegisterMetaType<FBXGeometry>();
static int fbxAnimationFrameMetaTypeId = qRegisterMetaType<FBXAnimationFrame>();
@ -512,6 +520,17 @@ QString processID(const QString& id) {
return id.mid(id.lastIndexOf(':') + 1);
}
QString getName(const QVariantList& properties) {
QString name;
if (properties.size() == 3) {
name = properties.at(1).toString();
name = processID(name.left(name.indexOf(QChar('\0'))));
} else {
name = processID(properties.at(0).toString());
}
return name;
}
QString getID(const QVariantList& properties, int index = 0) {
return processID(properties.at(index).toString());
}
@ -869,7 +888,7 @@ ExtractedMesh extractMesh(const FBXNode& object) {
beginIndex = endIndex;
}
}
return data.extracted;
}
@ -1008,6 +1027,10 @@ bool checkMaterialsHaveTextures(const QHash<QString, Material>& materials,
FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) {
QHash<QString, ExtractedMesh> meshes;
QHash<QString, QString> modelIDsToNames;
QHash<QString, int> meshIDsToMeshIndices;
QHash<QString, QString> ooChildToParent;
QVector<ExtractedBlendshape> blendshapes;
QMultiHash<QString, QString> parentMap;
QMultiHash<QString, QString> childMap;
@ -1078,6 +1101,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
FBXGeometry geometry;
float unitScaleFactor = 1.0f;
foreach (const FBXNode& child, node.children) {
if (child.name == "FBXHeaderExtension") {
foreach (const FBXNode& object, child.children) {
if (object.name == "SceneInfo") {
@ -1115,20 +1139,15 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
if (object.name == "Geometry") {
if (object.properties.at(2) == "Mesh") {
meshes.insert(getID(object.properties), extractMesh(object));
} else { // object.properties.at(2) == "Shape"
ExtractedBlendshape extracted = { getID(object.properties), extractBlendshape(object) };
blendshapes.append(extracted);
}
} else if (object.name == "Model") {
QString name;
if (object.properties.size() == 3) {
name = object.properties.at(1).toString();
name = processID(name.left(name.indexOf(QChar('\0'))));
QString name = getName(object.properties);
QString id = getID(object.properties);
modelIDsToNames.insert(id, name);
} else {
name = getID(object.properties);
}
if (name == jointEyeLeftName || name == "EyeL" || name == "joint_Leye") {
jointEyeLeftID = getID(object.properties);
@ -1392,6 +1411,11 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
} else if (child.name == "Connections") {
foreach (const FBXNode& connection, child.children) {
if (connection.name == "C" || connection.name == "Connect") {
if (connection.properties.at(0) == "OO") {
QString childID = getID(connection.properties, 1);
QString parentID = getID(connection.properties, 2);
ooChildToParent.insert(childID, parentID);
}
if (connection.properties.at(0) == "OP") {
QByteArray type = connection.properties.at(3).toByteArray().toLower();
if (type.contains("diffuse")) {
@ -1882,6 +1906,9 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
extracted.mesh.isEye = (maxJointIndex == geometry.leftEyeJointIndex || maxJointIndex == geometry.rightEyeJointIndex);
geometry.meshes.append(extracted.mesh);
int meshIndex = geometry.meshes.size() - 1;
meshIDsToMeshIndices.insert(it.key(), meshIndex);
}
// now that all joints have been scanned, compute a collision shape for each joint
@ -1981,6 +2008,22 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
geometry.sittingPoints.append(sittingPoint);
}
// attempt to map any meshes to a named model
for (QHash<QString, int>::const_iterator m = meshIDsToMeshIndices.constBegin();
m != meshIDsToMeshIndices.constEnd(); m++) {
const QString& meshID = m.key();
int meshIndex = m.value();
if (ooChildToParent.contains(meshID)) {
const QString& modelID = ooChildToParent.value(meshID);
if (modelIDsToNames.contains(modelID)) {
const QString& modelName = modelIDsToNames.value(modelID);
geometry.meshIndicesToModelNames.insert(meshIndex, modelName);
}
}
}
return geometry;
}

View file

@ -224,6 +224,12 @@ public:
/// Returns the unscaled extents of the model's mesh
Extents getUnscaledMeshExtents() const;
QHash<int, QString> meshIndicesToModelNames;
/// given a meshIndex this will return the name of the model that mesh belongs to if known
QString getModelNameOfMesh(int meshIndex) const;
};
Q_DECLARE_METATYPE(FBXGeometry)

View file

@ -33,7 +33,7 @@ public:
virtual void renderElement(OctreeElement* element, RenderArgs* args) { /* swallow these */ }
virtual void init();
virtual void render(RenderMode renderMode = DEFAULT_RENDER_MODE) { /* swallow these */ }
virtual void render(RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE) { /* swallow these */ }
void setJurisdictionListener(JurisdictionListener* jurisdictionListener) { _jurisdictionListener = jurisdictionListener; }

View file

@ -14,6 +14,7 @@
#include <SharedUtil.h>
#include <PerfStat.h>
#include <RenderArgs.h>
#include "OctreeRenderer.h"
OctreeRenderer::OctreeRenderer() :
@ -161,7 +162,7 @@ bool OctreeRenderer::renderOperation(OctreeElement* element, void* extraData) {
return false;
}
void OctreeRenderer::render(RenderMode renderMode) {
void OctreeRenderer::render(RenderArgs::RenderMode renderMode) {
RenderArgs args = { this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust(), renderMode,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
if (_tree) {

View file

@ -18,6 +18,7 @@
#include <QObject>
#include <PacketHeaders.h>
#include <RenderArgs.h>
#include <SharedUtil.h>
#include "Octree.h"
@ -25,7 +26,6 @@
#include "ViewFrustum.h"
class OctreeRenderer;
class RenderArgs;
// Generic client side Octree renderer class.
@ -51,10 +51,8 @@ public:
/// initialize and GPU/rendering related resources
virtual void init();
enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE };
/// render the content of the octree
virtual void render(RenderMode renderMode = DEFAULT_RENDER_MODE);
virtual void render(RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE);
ViewFrustum* getViewFrustum() const { return _viewFrustum; }
void setViewFrustum(ViewFrustum* viewFrustum) { _viewFrustum = viewFrustum; }
@ -104,31 +102,4 @@ protected:
int _opaqueMeshPartsRendered;
};
class RenderArgs {
public:
OctreeRenderer* _renderer;
ViewFrustum* _viewFrustum;
float _sizeScale;
int _boundaryLevelAdjust;
OctreeRenderer::RenderMode _renderMode;
int _elementsTouched;
int _itemsRendered;
int _itemsOutOfView;
int _itemsTooSmall;
int _meshesConsidered;
int _meshesRendered;
int _meshesOutOfView;
int _meshesTooSmall;
int _materialSwitches;
int _trianglesRendered;
int _quadsRendered;
int _translucentMeshPartsRendered;
int _opaqueMeshPartsRendered;
};
#endif // hifi_OctreeRenderer_h

View file

@ -0,0 +1,46 @@
//
// RenderArgs.h
// libraries/shared
//
// Created by Brad Hefta-Gaub on 10/29/14.
// Copyright 2013-2014 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
//
#ifndef hifi_RenderArgs_h
#define hifi_RenderArgs_h
class ViewFrustum;
class OctreeRenderer;
class RenderArgs {
public:
enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE };
OctreeRenderer* _renderer;
ViewFrustum* _viewFrustum;
float _sizeScale;
int _boundaryLevelAdjust;
RenderMode _renderMode;
int _elementsTouched;
int _itemsRendered;
int _itemsOutOfView;
int _itemsTooSmall;
int _meshesConsidered;
int _meshesRendered;
int _meshesOutOfView;
int _meshesTooSmall;
int _materialSwitches;
int _trianglesRendered;
int _quadsRendered;
int _translucentMeshPartsRendered;
int _opaqueMeshPartsRendered;
};
#endif // hifi_RenderArgs_h