mirror of
https://github.com/overte-org/overte.git
synced 2025-08-08 13:58:51 +02:00
Merge pull request #3690 from ZappoMan/rayPickSubMeshes
Ray pick sub meshes
This commit is contained in:
commit
5b3897f1cf
41 changed files with 432 additions and 147 deletions
87
examples/testModelOverlaySubMeshes.js
Normal file
87
examples/testModelOverlaySubMeshes.js
Normal 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);
|
||||||
|
});
|
|
@ -2787,7 +2787,7 @@ void Application::updateShadowMap() {
|
||||||
|
|
||||||
{
|
{
|
||||||
PerformanceTimer perfTimer("entities");
|
PerformanceTimer perfTimer("entities");
|
||||||
_entities.render(OctreeRenderer::SHADOW_RENDER_MODE);
|
_entities.render(RenderArgs::SHADOW_RENDER_MODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <BoxEntityItem.h>
|
#include <BoxEntityItem.h>
|
||||||
#include <ModelEntityItem.h>
|
#include <ModelEntityItem.h>
|
||||||
#include <PerfStat.h>
|
#include <PerfStat.h>
|
||||||
|
#include <RenderArgs.h>
|
||||||
|
|
||||||
|
|
||||||
#include "Menu.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);
|
OctreeRenderer::render(renderMode);
|
||||||
deleteReleasedModels(); // seems like as good as any other place to do some memory cleanup
|
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) {
|
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);
|
bool displayModelBounds = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelBounds);
|
||||||
if (!isShadowMode && displayModelBounds) {
|
if (!isShadowMode && displayModelBounds) {
|
||||||
PerformanceTimer perfTimer("renderProxies");
|
PerformanceTimer perfTimer("renderProxies");
|
||||||
|
@ -239,7 +240,7 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
|
||||||
|
|
||||||
uint16_t numberOfEntities = entityItems.size();
|
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);
|
bool displayElementProxy = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelElementProxy);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <OctreePacketData.h>
|
#include <OctreePacketData.h>
|
||||||
#include <OctreeRenderer.h>
|
#include <OctreeRenderer.h>
|
||||||
#include <PacketHeaders.h>
|
#include <PacketHeaders.h>
|
||||||
|
#include <RenderArgs.h>
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
#include <ViewFrustum.h>
|
#include <ViewFrustum.h>
|
||||||
|
|
||||||
|
@ -48,7 +49,7 @@ public:
|
||||||
void processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode);
|
void processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode);
|
||||||
|
|
||||||
virtual void init();
|
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 FBXGeometry* getGeometryForEntity(const EntityItem* entityItem);
|
||||||
virtual const Model* getModelForEntityItem(const EntityItem* entityItem);
|
virtual const Model* getModelForEntityItem(const EntityItem* entityItem);
|
||||||
|
|
|
@ -112,7 +112,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: should we allow entityItems to have alpha on their models?
|
// 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;
|
? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
|
||||||
|
|
||||||
if (_model->isActive()) {
|
if (_model->isActive()) {
|
||||||
|
|
|
@ -36,6 +36,7 @@ using namespace std;
|
||||||
static int modelPointerTypeId = qRegisterMetaType<QPointer<Model> >();
|
static int modelPointerTypeId = qRegisterMetaType<QPointer<Model> >();
|
||||||
static int weakNetworkGeometryPointerTypeId = qRegisterMetaType<QWeakPointer<NetworkGeometry> >();
|
static int weakNetworkGeometryPointerTypeId = qRegisterMetaType<QWeakPointer<NetworkGeometry> >();
|
||||||
static int vec3VectorTypeId = qRegisterMetaType<QVector<glm::vec3> >();
|
static int vec3VectorTypeId = qRegisterMetaType<QVector<glm::vec3> >();
|
||||||
|
float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f;
|
||||||
|
|
||||||
Model::Model(QObject* parent) :
|
Model::Model(QObject* parent) :
|
||||||
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) {
|
void Model::initProgram(ProgramObject& program, Model::Locations& locations, int specularTextureUnit) {
|
||||||
program.bind();
|
program.bind();
|
||||||
|
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
// HACK: Assign explicitely the attribute channel to avoid a bug on Yosemite
|
|
||||||
glBindAttribLocation(program.programId(), 4, "tangent");
|
// HACK: Assign explicitely the attribute channel to avoid a bug on Yosemite
|
||||||
glLinkProgram(program.programId());
|
|
||||||
#endif
|
glBindAttribLocation(program.programId(), 4, "tangent");
|
||||||
|
|
||||||
locations.tangent = program.attributeLocation("tangent");
|
glLinkProgram(program.programId());
|
||||||
locations.alphaThreshold = program.uniformLocation("alphaThreshold");
|
|
||||||
program.setUniformValue("diffuseMap", 0);
|
#endif
|
||||||
program.setUniformValue("normalMap", 1);
|
|
||||||
program.setUniformValue("specularMap", specularTextureUnit);
|
|
||||||
program.release();
|
|
||||||
}
|
locations.tangent = program.attributeLocation("tangent");
|
||||||
|
|
||||||
void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locations, int specularTextureUnit) {
|
locations.alphaThreshold = program.uniformLocation("alphaThreshold");
|
||||||
initProgram(program, locations, specularTextureUnit);
|
|
||||||
|
program.setUniformValue("diffuseMap", 0);
|
||||||
#ifdef Q_OS_MAC
|
|
||||||
// HACK: Assign explicitely the attribute channel to avoid a bug on Yosemite
|
program.setUniformValue("normalMap", 1);
|
||||||
glBindAttribLocation(program.programId(), 5, "clusterIndices");
|
|
||||||
glBindAttribLocation(program.programId(), 6, "clusterWeights");
|
program.setUniformValue("specularMap", specularTextureUnit);
|
||||||
glLinkProgram(program.programId());
|
|
||||||
#endif
|
program.release();
|
||||||
|
|
||||||
program.bind();
|
}
|
||||||
locations.clusterMatrices = program.uniformLocation("clusterMatrices");
|
|
||||||
|
|
||||||
locations.clusterIndices = program.attributeLocation("clusterIndices");
|
|
||||||
locations.clusterWeights = program.attributeLocation("clusterWeights");
|
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();
|
program.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,6 +440,80 @@ void Model::setJointStates(QVector<JointState> states) {
|
||||||
_boundingRadius = radius;
|
_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) {
|
bool Model::render(float alpha, RenderMode mode, RenderArgs* args) {
|
||||||
PROFILE_RANGE(__FUNCTION__);
|
PROFILE_RANGE(__FUNCTION__);
|
||||||
// render the attachments
|
// 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
|
// against. We cache the results of these calculations so long as the model hasn't been
|
||||||
// simulated and the mesh hasn't changed.
|
// simulated and the mesh hasn't changed.
|
||||||
if (args && !_calculatedMeshBoxesValid) {
|
if (args && !_calculatedMeshBoxesValid) {
|
||||||
PerformanceTimer perfTimer("calculatedMeshBoxes");
|
recalcuateMeshBoxes();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// set up dilated textures on first render after load/simulate
|
// 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) {
|
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()) {
|
if (!isActive()) {
|
||||||
|
_scaleToFit = scaleToFit;
|
||||||
|
if (scaleToFit) {
|
||||||
|
_scaleToFitDimensions = glm::vec3(largestDimension, FAKE_DIMENSION_PLACEHOLDER, FAKE_DIMENSION_PLACEHOLDER);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -896,6 +999,15 @@ void Model::setScaleToFit(bool scaleToFit, float largestDimension) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Model::scaleToFit() {
|
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();
|
Extents modelMeshExtents = getUnscaledMeshExtents();
|
||||||
|
|
||||||
// size is our "target size in world space"
|
// size is our "target size in world space"
|
||||||
|
|
|
@ -189,13 +189,18 @@ public:
|
||||||
|
|
||||||
Q_INVOKABLE void setTextureWithNameToURL(const QString& name, const QUrl& url)
|
Q_INVOKABLE void setTextureWithNameToURL(const QString& name, const QUrl& url)
|
||||||
{ _geometry->setTextureWithNameToURL(name, url); }
|
{ _geometry->setTextureWithNameToURL(name, url); }
|
||||||
|
|
||||||
|
bool findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
|
float& distance, BoxFace& face, QString& extraInfo) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QSharedPointer<NetworkGeometry> _geometry;
|
QSharedPointer<NetworkGeometry> _geometry;
|
||||||
|
|
||||||
glm::vec3 _scale;
|
glm::vec3 _scale;
|
||||||
glm::vec3 _offset;
|
glm::vec3 _offset;
|
||||||
|
|
||||||
|
static float FAKE_DIMENSION_PLACEHOLDER;
|
||||||
|
|
||||||
bool _scaleToFit; /// If you set scaleToFit, we will calculate scale based on MeshExtents
|
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
|
glm::vec3 _scaleToFitDimensions; /// this is the dimensions that scale to fit will use
|
||||||
bool _scaledToFit; /// have we scaled to fit
|
bool _scaledToFit; /// have we scaled to fit
|
||||||
|
@ -341,6 +346,8 @@ private:
|
||||||
QVector<AABox> _calculatedMeshBoxes;
|
QVector<AABox> _calculatedMeshBoxes;
|
||||||
bool _calculatedMeshBoxesValid;
|
bool _calculatedMeshBoxesValid;
|
||||||
|
|
||||||
|
void recalcuateMeshBoxes();
|
||||||
|
|
||||||
void segregateMeshGroups(); // used to calculate our list of translucent vs opaque meshes
|
void segregateMeshGroups(); // used to calculate our list of translucent vs opaque meshes
|
||||||
|
|
||||||
bool _meshGroupsKnown;
|
bool _meshGroupsKnown;
|
||||||
|
|
|
@ -48,6 +48,11 @@ public:
|
||||||
|
|
||||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const;
|
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:
|
protected:
|
||||||
void drawDashedLine(const glm::vec3& start, const glm::vec3& end);
|
void drawDashedLine(const glm::vec3& start, const glm::vec3& end);
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ BillboardOverlay::BillboardOverlay() :
|
||||||
_isLoaded = false;
|
_isLoaded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BillboardOverlay::render() {
|
void BillboardOverlay::render(RenderArgs* args) {
|
||||||
if (!_visible || !_isLoaded) {
|
if (!_visible || !_isLoaded) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ class BillboardOverlay : public Base3DOverlay {
|
||||||
public:
|
public:
|
||||||
BillboardOverlay();
|
BillboardOverlay();
|
||||||
|
|
||||||
virtual void render();
|
virtual void render(RenderArgs* args);
|
||||||
|
|
||||||
// setters
|
// setters
|
||||||
void setURL(const QString& url);
|
void setURL(const QString& url);
|
||||||
|
|
|
@ -36,7 +36,7 @@ Circle3DOverlay::Circle3DOverlay() :
|
||||||
Circle3DOverlay::~Circle3DOverlay() {
|
Circle3DOverlay::~Circle3DOverlay() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Circle3DOverlay::render() {
|
void Circle3DOverlay::render(RenderArgs* args) {
|
||||||
if (!_visible) {
|
if (!_visible) {
|
||||||
return; // do nothing if we're not visible
|
return; // do nothing if we're not visible
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ class Circle3DOverlay : public Planar3DOverlay {
|
||||||
public:
|
public:
|
||||||
Circle3DOverlay();
|
Circle3DOverlay();
|
||||||
~Circle3DOverlay();
|
~Circle3DOverlay();
|
||||||
virtual void render();
|
virtual void render(RenderArgs* args);
|
||||||
virtual void setProperties(const QScriptValue& properties);
|
virtual void setProperties(const QScriptValue& properties);
|
||||||
|
|
||||||
float getStartAt() const { return _startAt; }
|
float getStartAt() const { return _startAt; }
|
||||||
|
|
|
@ -25,7 +25,7 @@ Cube3DOverlay::Cube3DOverlay() {
|
||||||
Cube3DOverlay::~Cube3DOverlay() {
|
Cube3DOverlay::~Cube3DOverlay() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cube3DOverlay::render() {
|
void Cube3DOverlay::render(RenderArgs* args) {
|
||||||
if (!_visible) {
|
if (!_visible) {
|
||||||
return; // do nothing if we're not visible
|
return; // do nothing if we're not visible
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ class Cube3DOverlay : public Volume3DOverlay {
|
||||||
public:
|
public:
|
||||||
Cube3DOverlay();
|
Cube3DOverlay();
|
||||||
~Cube3DOverlay();
|
~Cube3DOverlay();
|
||||||
virtual void render();
|
virtual void render(RenderArgs* args);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ void ImageOverlay::replyFinished() {
|
||||||
_isLoaded = true;
|
_isLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageOverlay::render() {
|
void ImageOverlay::render(RenderArgs* args) {
|
||||||
if (!_visible || !_isLoaded) {
|
if (!_visible || !_isLoaded) {
|
||||||
return; // do nothing if we're not visible
|
return; // do nothing if we're not visible
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ class ImageOverlay : public Overlay2D {
|
||||||
public:
|
public:
|
||||||
ImageOverlay();
|
ImageOverlay();
|
||||||
~ImageOverlay();
|
~ImageOverlay();
|
||||||
virtual void render();
|
virtual void render(RenderArgs* args);
|
||||||
|
|
||||||
// getters
|
// getters
|
||||||
const QRect& getClipFromSource() const { return _fromImage; }
|
const QRect& getClipFromSource() const { return _fromImage; }
|
||||||
|
|
|
@ -21,7 +21,7 @@ Line3DOverlay::Line3DOverlay() {
|
||||||
Line3DOverlay::~Line3DOverlay() {
|
Line3DOverlay::~Line3DOverlay() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Line3DOverlay::render() {
|
void Line3DOverlay::render(RenderArgs* args) {
|
||||||
if (!_visible) {
|
if (!_visible) {
|
||||||
return; // do nothing if we're not visible
|
return; // do nothing if we're not visible
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ class Line3DOverlay : public Base3DOverlay {
|
||||||
public:
|
public:
|
||||||
Line3DOverlay();
|
Line3DOverlay();
|
||||||
~Line3DOverlay();
|
~Line3DOverlay();
|
||||||
virtual void render();
|
virtual void render(RenderArgs* args);
|
||||||
|
|
||||||
// getters
|
// getters
|
||||||
const glm::vec3& getEnd() const { return _end; }
|
const glm::vec3& getEnd() const { return _end; }
|
||||||
|
|
|
@ -25,7 +25,7 @@ void LocalModelsOverlay::update(float deltatime) {
|
||||||
_entityTreeRenderer->update();
|
_entityTreeRenderer->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalModelsOverlay::render() {
|
void LocalModelsOverlay::render(RenderArgs* args) {
|
||||||
if (_visible) {
|
if (_visible) {
|
||||||
|
|
||||||
float glowLevel = getGlowLevel();
|
float glowLevel = getGlowLevel();
|
||||||
|
|
|
@ -23,7 +23,7 @@ public:
|
||||||
~LocalModelsOverlay();
|
~LocalModelsOverlay();
|
||||||
|
|
||||||
virtual void update(float deltatime);
|
virtual void update(float deltatime);
|
||||||
virtual void render();
|
virtual void render(RenderArgs* args);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
EntityTreeRenderer* _entityTreeRenderer;
|
EntityTreeRenderer* _entityTreeRenderer;
|
||||||
|
|
|
@ -51,7 +51,7 @@ void LocalVoxelsOverlay::update(float deltatime) {
|
||||||
_tree->unlock();
|
_tree->unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalVoxelsOverlay::render() {
|
void LocalVoxelsOverlay::render(RenderArgs* args) {
|
||||||
glm::vec3 dimensions = getDimensions();
|
glm::vec3 dimensions = getDimensions();
|
||||||
float size = glm::length(dimensions);
|
float size = glm::length(dimensions);
|
||||||
if (_visible && size > 0 && _voxelSystem && _voxelSystem->isInitialized()) {
|
if (_visible && size > 0 && _voxelSystem && _voxelSystem->isInitialized()) {
|
||||||
|
|
|
@ -35,7 +35,7 @@ public:
|
||||||
~LocalVoxelsOverlay();
|
~LocalVoxelsOverlay();
|
||||||
|
|
||||||
virtual void update(float deltatime);
|
virtual void update(float deltatime);
|
||||||
virtual void render();
|
virtual void render(RenderArgs* args);
|
||||||
|
|
||||||
virtual void setProperties(const QScriptValue& properties);
|
virtual void setProperties(const QScriptValue& properties);
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@ void ModelOverlay::update(float deltatime) {
|
||||||
if (_updateModel) {
|
if (_updateModel) {
|
||||||
_updateModel = false;
|
_updateModel = false;
|
||||||
|
|
||||||
_model.setScaleToFit(true, _scale);
|
|
||||||
_model.setSnapModelToCenter(true);
|
_model.setSnapModelToCenter(true);
|
||||||
_model.setRotation(_rotation);
|
_model.setRotation(_rotation);
|
||||||
_model.setTranslation(_position);
|
_model.setTranslation(_position);
|
||||||
|
@ -37,7 +36,7 @@ void ModelOverlay::update(float deltatime) {
|
||||||
_isLoaded = _model.isActive();
|
_isLoaded = _model.isActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelOverlay::render() {
|
void ModelOverlay::render(RenderArgs* args) {
|
||||||
if (!_visible) {
|
if (!_visible) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -49,7 +48,7 @@ void ModelOverlay::render() {
|
||||||
if (glowLevel > 0.0f) {
|
if (glowLevel > 0.0f) {
|
||||||
glower = new Glower(glowLevel);
|
glower = new Glower(glowLevel);
|
||||||
}
|
}
|
||||||
_model.render(getAlpha());
|
_model.render(getAlpha(), Model::DEFAULT_RENDER_MODE, args);
|
||||||
if (glower) {
|
if (glower) {
|
||||||
delete glower;
|
delete glower;
|
||||||
}
|
}
|
||||||
|
@ -70,6 +69,7 @@ void ModelOverlay::setProperties(const QScriptValue &properties) {
|
||||||
QScriptValue scaleValue = properties.property("scale");
|
QScriptValue scaleValue = properties.property("scale");
|
||||||
if (scaleValue.isValid()) {
|
if (scaleValue.isValid()) {
|
||||||
_scale = scaleValue.toVariant().toFloat();
|
_scale = scaleValue.toVariant().toFloat();
|
||||||
|
_model.setScaleToFit(true, _scale);
|
||||||
_updateModel = true;
|
_updateModel = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,33 +124,15 @@ void ModelOverlay::setProperties(const QScriptValue &properties) {
|
||||||
|
|
||||||
bool ModelOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
bool ModelOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
float& distance, BoxFace& face) const {
|
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;
|
QString subMeshNameTemp;
|
||||||
glm::vec3 corner = dimensions * -0.5f; // since we're going to do the ray picking in the overlay frame of reference
|
return _model.findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, subMeshNameTemp);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -22,9 +22,11 @@ public:
|
||||||
ModelOverlay();
|
ModelOverlay();
|
||||||
|
|
||||||
virtual void update(float deltatime);
|
virtual void update(float deltatime);
|
||||||
virtual void render();
|
virtual void render(RenderArgs* args);
|
||||||
virtual void setProperties(const QScriptValue& properties);
|
virtual void setProperties(const QScriptValue& properties);
|
||||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const;
|
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:
|
private:
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include <SharedUtil.h> // for xColor
|
#include <SharedUtil.h> // for xColor
|
||||||
|
#include <RenderArgs.h>
|
||||||
|
|
||||||
const xColor DEFAULT_OVERLAY_COLOR = { 255, 255, 255 };
|
const xColor DEFAULT_OVERLAY_COLOR = { 255, 255, 255 };
|
||||||
const float DEFAULT_ALPHA = 0.7f;
|
const float DEFAULT_ALPHA = 0.7f;
|
||||||
|
@ -37,7 +38,7 @@ public:
|
||||||
~Overlay();
|
~Overlay();
|
||||||
void init(QGLWidget* parent);
|
void init(QGLWidget* parent);
|
||||||
virtual void update(float deltatime) {}
|
virtual void update(float deltatime) {}
|
||||||
virtual void render() = 0;
|
virtual void render(RenderArgs* args) = 0;
|
||||||
|
|
||||||
// getters
|
// getters
|
||||||
virtual bool is3D() const = 0;
|
virtual bool is3D() const = 0;
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <Application.h>
|
#include <Application.h>
|
||||||
|
#include <Menu.h>
|
||||||
|
|
||||||
#include "BillboardOverlay.h"
|
#include "BillboardOverlay.h"
|
||||||
#include "Circle3DOverlay.h"
|
#include "Circle3DOverlay.h"
|
||||||
|
@ -78,8 +79,13 @@ void Overlays::update(float deltatime) {
|
||||||
|
|
||||||
void Overlays::render2D() {
|
void Overlays::render2D() {
|
||||||
QReadLocker lock(&_lock);
|
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) {
|
foreach(Overlay* thisOverlay, _overlays2D) {
|
||||||
thisOverlay->render();
|
thisOverlay->render(&args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,6 +101,11 @@ void Overlays::render3D() {
|
||||||
float angle = 0.0f;
|
float angle = 0.0f;
|
||||||
glm::vec3 axis(0.0f, 1.0f, 0.0f);
|
glm::vec3 axis(0.0f, 1.0f, 0.0f);
|
||||||
float myAvatarScale = 1.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) {
|
foreach(Overlay* thisOverlay, _overlays3D) {
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
|
@ -118,7 +129,7 @@ void Overlays::render3D() {
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
thisOverlay->render();
|
thisOverlay->render(&args);
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -239,7 +250,8 @@ RayToOverlayIntersectionResult Overlays::findRayIntersection(const PickRay& ray)
|
||||||
if (thisOverlay->getVisible() && !thisOverlay->getIgnoreRayIntersection() && thisOverlay->isLoaded()) {
|
if (thisOverlay->getVisible() && !thisOverlay->getIgnoreRayIntersection() && thisOverlay->isLoaded()) {
|
||||||
float thisDistance;
|
float thisDistance;
|
||||||
BoxFace thisFace;
|
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) {
|
if (thisDistance < bestDistance) {
|
||||||
bestDistance = thisDistance;
|
bestDistance = thisDistance;
|
||||||
result.intersects = true;
|
result.intersects = true;
|
||||||
|
@ -247,6 +259,7 @@ RayToOverlayIntersectionResult Overlays::findRayIntersection(const PickRay& ray)
|
||||||
result.face = thisFace;
|
result.face = thisFace;
|
||||||
result.overlayID = thisID;
|
result.overlayID = thisID;
|
||||||
result.intersection = ray.origin + (ray.direction * thisDistance);
|
result.intersection = ray.origin + (ray.direction * thisDistance);
|
||||||
|
result.extraInfo = thisExtraInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -259,7 +272,8 @@ RayToOverlayIntersectionResult::RayToOverlayIntersectionResult() :
|
||||||
overlayID(-1),
|
overlayID(-1),
|
||||||
distance(0),
|
distance(0),
|
||||||
face(),
|
face(),
|
||||||
intersection()
|
intersection(),
|
||||||
|
extraInfo()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,6 +312,7 @@ QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine,
|
||||||
obj.setProperty("face", faceName);
|
obj.setProperty("face", faceName);
|
||||||
QScriptValue intersection = vec3toScriptValue(engine, value.intersection);
|
QScriptValue intersection = vec3toScriptValue(engine, value.intersection);
|
||||||
obj.setProperty("intersection", intersection);
|
obj.setProperty("intersection", intersection);
|
||||||
|
obj.setProperty("extraInfo", value.extraInfo);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,6 +341,7 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R
|
||||||
if (intersection.isValid()) {
|
if (intersection.isValid()) {
|
||||||
vec3FromScriptValue(intersection, value.intersection);
|
vec3FromScriptValue(intersection, value.intersection);
|
||||||
}
|
}
|
||||||
|
value.extraInfo = object.property("extraInfo").toVariant().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Overlays::isLoaded(unsigned int id) {
|
bool Overlays::isLoaded(unsigned int id) {
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#ifndef hifi_Overlays_h
|
#ifndef hifi_Overlays_h
|
||||||
#define hifi_Overlays_h
|
#define hifi_Overlays_h
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
#include <QScriptValue>
|
#include <QScriptValue>
|
||||||
|
|
||||||
#include "Overlay.h"
|
#include "Overlay.h"
|
||||||
|
@ -23,6 +24,7 @@ public:
|
||||||
float distance;
|
float distance;
|
||||||
BoxFace face;
|
BoxFace face;
|
||||||
glm::vec3 intersection;
|
glm::vec3 intersection;
|
||||||
|
QString extraInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(RayToOverlayIntersectionResult);
|
Q_DECLARE_METATYPE(RayToOverlayIntersectionResult);
|
||||||
|
|
|
@ -23,7 +23,7 @@ Rectangle3DOverlay::Rectangle3DOverlay() {
|
||||||
Rectangle3DOverlay::~Rectangle3DOverlay() {
|
Rectangle3DOverlay::~Rectangle3DOverlay() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rectangle3DOverlay::render() {
|
void Rectangle3DOverlay::render(RenderArgs* args) {
|
||||||
if (!_visible) {
|
if (!_visible) {
|
||||||
return; // do nothing if we're not visible
|
return; // do nothing if we're not visible
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ class Rectangle3DOverlay : public Planar3DOverlay {
|
||||||
public:
|
public:
|
||||||
Rectangle3DOverlay();
|
Rectangle3DOverlay();
|
||||||
~Rectangle3DOverlay();
|
~Rectangle3DOverlay();
|
||||||
virtual void render();
|
virtual void render(RenderArgs* args);
|
||||||
virtual void setProperties(const QScriptValue& properties);
|
virtual void setProperties(const QScriptValue& properties);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ Sphere3DOverlay::Sphere3DOverlay() {
|
||||||
Sphere3DOverlay::~Sphere3DOverlay() {
|
Sphere3DOverlay::~Sphere3DOverlay() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sphere3DOverlay::render() {
|
void Sphere3DOverlay::render(RenderArgs* args) {
|
||||||
if (!_visible) {
|
if (!_visible) {
|
||||||
return; // do nothing if we're not visible
|
return; // do nothing if we're not visible
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ class Sphere3DOverlay : public Volume3DOverlay {
|
||||||
public:
|
public:
|
||||||
Sphere3DOverlay();
|
Sphere3DOverlay();
|
||||||
~Sphere3DOverlay();
|
~Sphere3DOverlay();
|
||||||
virtual void render();
|
virtual void render(RenderArgs* args);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ xColor Text3DOverlay::getBackgroundColor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Text3DOverlay::render() {
|
void Text3DOverlay::render(RenderArgs* args) {
|
||||||
if (!_visible) {
|
if (!_visible) {
|
||||||
return; // do nothing if we're not visible
|
return; // do nothing if we're not visible
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
#include "InterfaceConfig.h"
|
#include "InterfaceConfig.h"
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
#include <RenderArgs.h>
|
||||||
#include "Planar3DOverlay.h"
|
#include "Planar3DOverlay.h"
|
||||||
|
|
||||||
class Text3DOverlay : public Planar3DOverlay {
|
class Text3DOverlay : public Planar3DOverlay {
|
||||||
|
@ -23,7 +25,7 @@ class Text3DOverlay : public Planar3DOverlay {
|
||||||
public:
|
public:
|
||||||
Text3DOverlay();
|
Text3DOverlay();
|
||||||
~Text3DOverlay();
|
~Text3DOverlay();
|
||||||
virtual void render();
|
virtual void render(RenderArgs* args);
|
||||||
|
|
||||||
// getters
|
// getters
|
||||||
const QString& getText() const { return _text; }
|
const QString& getText() const { return _text; }
|
||||||
|
|
|
@ -48,7 +48,7 @@ xColor TextOverlay::getBackgroundColor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void TextOverlay::render() {
|
void TextOverlay::render(RenderArgs* args) {
|
||||||
if (!_visible) {
|
if (!_visible) {
|
||||||
return; // do nothing if we're not visible
|
return; // do nothing if we're not visible
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ class TextOverlay : public Overlay2D {
|
||||||
public:
|
public:
|
||||||
TextOverlay();
|
TextOverlay();
|
||||||
~TextOverlay();
|
~TextOverlay();
|
||||||
virtual void render();
|
virtual void render(RenderArgs* args);
|
||||||
|
|
||||||
// getters
|
// getters
|
||||||
const QString& getText() const { return _text; }
|
const QString& getText() const { return _text; }
|
||||||
|
|
|
@ -70,6 +70,14 @@ Extents FBXGeometry::getUnscaledMeshExtents() const {
|
||||||
return scaledExtents;
|
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 fbxGeometryMetaTypeId = qRegisterMetaType<FBXGeometry>();
|
||||||
static int fbxAnimationFrameMetaTypeId = qRegisterMetaType<FBXAnimationFrame>();
|
static int fbxAnimationFrameMetaTypeId = qRegisterMetaType<FBXAnimationFrame>();
|
||||||
|
@ -512,6 +520,17 @@ QString processID(const QString& id) {
|
||||||
return id.mid(id.lastIndexOf(':') + 1);
|
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) {
|
QString getID(const QVariantList& properties, int index = 0) {
|
||||||
return processID(properties.at(index).toString());
|
return processID(properties.at(index).toString());
|
||||||
}
|
}
|
||||||
|
@ -869,7 +888,7 @@ ExtractedMesh extractMesh(const FBXNode& object) {
|
||||||
beginIndex = endIndex;
|
beginIndex = endIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return data.extracted;
|
return data.extracted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1008,6 +1027,10 @@ bool checkMaterialsHaveTextures(const QHash<QString, Material>& materials,
|
||||||
|
|
||||||
FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) {
|
FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) {
|
||||||
QHash<QString, ExtractedMesh> meshes;
|
QHash<QString, ExtractedMesh> meshes;
|
||||||
|
QHash<QString, QString> modelIDsToNames;
|
||||||
|
QHash<QString, int> meshIDsToMeshIndices;
|
||||||
|
QHash<QString, QString> ooChildToParent;
|
||||||
|
|
||||||
QVector<ExtractedBlendshape> blendshapes;
|
QVector<ExtractedBlendshape> blendshapes;
|
||||||
QMultiHash<QString, QString> parentMap;
|
QMultiHash<QString, QString> parentMap;
|
||||||
QMultiHash<QString, QString> childMap;
|
QMultiHash<QString, QString> childMap;
|
||||||
|
@ -1078,6 +1101,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
||||||
FBXGeometry geometry;
|
FBXGeometry geometry;
|
||||||
float unitScaleFactor = 1.0f;
|
float unitScaleFactor = 1.0f;
|
||||||
foreach (const FBXNode& child, node.children) {
|
foreach (const FBXNode& child, node.children) {
|
||||||
|
|
||||||
if (child.name == "FBXHeaderExtension") {
|
if (child.name == "FBXHeaderExtension") {
|
||||||
foreach (const FBXNode& object, child.children) {
|
foreach (const FBXNode& object, child.children) {
|
||||||
if (object.name == "SceneInfo") {
|
if (object.name == "SceneInfo") {
|
||||||
|
@ -1115,20 +1139,15 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
||||||
if (object.name == "Geometry") {
|
if (object.name == "Geometry") {
|
||||||
if (object.properties.at(2) == "Mesh") {
|
if (object.properties.at(2) == "Mesh") {
|
||||||
meshes.insert(getID(object.properties), extractMesh(object));
|
meshes.insert(getID(object.properties), extractMesh(object));
|
||||||
|
|
||||||
} else { // object.properties.at(2) == "Shape"
|
} else { // object.properties.at(2) == "Shape"
|
||||||
ExtractedBlendshape extracted = { getID(object.properties), extractBlendshape(object) };
|
ExtractedBlendshape extracted = { getID(object.properties), extractBlendshape(object) };
|
||||||
blendshapes.append(extracted);
|
blendshapes.append(extracted);
|
||||||
}
|
}
|
||||||
} else if (object.name == "Model") {
|
} else if (object.name == "Model") {
|
||||||
QString name;
|
QString name = getName(object.properties);
|
||||||
if (object.properties.size() == 3) {
|
QString id = getID(object.properties);
|
||||||
name = object.properties.at(1).toString();
|
modelIDsToNames.insert(id, name);
|
||||||
name = processID(name.left(name.indexOf(QChar('\0'))));
|
|
||||||
|
|
||||||
} else {
|
|
||||||
name = getID(object.properties);
|
|
||||||
}
|
|
||||||
if (name == jointEyeLeftName || name == "EyeL" || name == "joint_Leye") {
|
if (name == jointEyeLeftName || name == "EyeL" || name == "joint_Leye") {
|
||||||
jointEyeLeftID = getID(object.properties);
|
jointEyeLeftID = getID(object.properties);
|
||||||
|
|
||||||
|
@ -1392,6 +1411,11 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
||||||
} else if (child.name == "Connections") {
|
} else if (child.name == "Connections") {
|
||||||
foreach (const FBXNode& connection, child.children) {
|
foreach (const FBXNode& connection, child.children) {
|
||||||
if (connection.name == "C" || connection.name == "Connect") {
|
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") {
|
if (connection.properties.at(0) == "OP") {
|
||||||
QByteArray type = connection.properties.at(3).toByteArray().toLower();
|
QByteArray type = connection.properties.at(3).toByteArray().toLower();
|
||||||
if (type.contains("diffuse")) {
|
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);
|
extracted.mesh.isEye = (maxJointIndex == geometry.leftEyeJointIndex || maxJointIndex == geometry.rightEyeJointIndex);
|
||||||
|
|
||||||
geometry.meshes.append(extracted.mesh);
|
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
|
// 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);
|
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;
|
return geometry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -224,6 +224,12 @@ public:
|
||||||
|
|
||||||
/// Returns the unscaled extents of the model's mesh
|
/// Returns the unscaled extents of the model's mesh
|
||||||
Extents getUnscaledMeshExtents() const;
|
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)
|
Q_DECLARE_METATYPE(FBXGeometry)
|
||||||
|
|
|
@ -33,7 +33,7 @@ public:
|
||||||
virtual void renderElement(OctreeElement* element, RenderArgs* args) { /* swallow these */ }
|
virtual void renderElement(OctreeElement* element, RenderArgs* args) { /* swallow these */ }
|
||||||
|
|
||||||
virtual void init();
|
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; }
|
void setJurisdictionListener(JurisdictionListener* jurisdictionListener) { _jurisdictionListener = jurisdictionListener; }
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
#include <PerfStat.h>
|
#include <PerfStat.h>
|
||||||
|
#include <RenderArgs.h>
|
||||||
#include "OctreeRenderer.h"
|
#include "OctreeRenderer.h"
|
||||||
|
|
||||||
OctreeRenderer::OctreeRenderer() :
|
OctreeRenderer::OctreeRenderer() :
|
||||||
|
@ -161,7 +162,7 @@ bool OctreeRenderer::renderOperation(OctreeElement* element, void* extraData) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OctreeRenderer::render(RenderMode renderMode) {
|
void OctreeRenderer::render(RenderArgs::RenderMode renderMode) {
|
||||||
RenderArgs args = { this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust(), renderMode,
|
RenderArgs args = { this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust(), renderMode,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||||
if (_tree) {
|
if (_tree) {
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include <PacketHeaders.h>
|
#include <PacketHeaders.h>
|
||||||
|
#include <RenderArgs.h>
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
|
|
||||||
#include "Octree.h"
|
#include "Octree.h"
|
||||||
|
@ -25,7 +26,6 @@
|
||||||
#include "ViewFrustum.h"
|
#include "ViewFrustum.h"
|
||||||
|
|
||||||
class OctreeRenderer;
|
class OctreeRenderer;
|
||||||
class RenderArgs;
|
|
||||||
|
|
||||||
|
|
||||||
// Generic client side Octree renderer class.
|
// Generic client side Octree renderer class.
|
||||||
|
@ -51,10 +51,8 @@ public:
|
||||||
/// initialize and GPU/rendering related resources
|
/// initialize and GPU/rendering related resources
|
||||||
virtual void init();
|
virtual void init();
|
||||||
|
|
||||||
enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE };
|
|
||||||
|
|
||||||
/// render the content of the octree
|
/// 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; }
|
ViewFrustum* getViewFrustum() const { return _viewFrustum; }
|
||||||
void setViewFrustum(ViewFrustum* viewFrustum) { _viewFrustum = viewFrustum; }
|
void setViewFrustum(ViewFrustum* viewFrustum) { _viewFrustum = viewFrustum; }
|
||||||
|
@ -104,31 +102,4 @@ protected:
|
||||||
int _opaqueMeshPartsRendered;
|
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
|
#endif // hifi_OctreeRenderer_h
|
||||||
|
|
46
libraries/shared/src/RenderArgs.h
Normal file
46
libraries/shared/src/RenderArgs.h
Normal 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
|
Loading…
Reference in a new issue