mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 18:23:54 +02:00
Moved spanners into separate file, working on heightfields as spanners.
This commit is contained in:
parent
a77525209d
commit
0664ff3181
10 changed files with 1264 additions and 838 deletions
|
@ -36,6 +36,7 @@ REGISTER_META_OBJECT(DefaultMetavoxelRendererImplementation)
|
|||
REGISTER_META_OBJECT(SphereRenderer)
|
||||
REGISTER_META_OBJECT(CuboidRenderer)
|
||||
REGISTER_META_OBJECT(StaticModelRenderer)
|
||||
REGISTER_META_OBJECT(HeightfieldRenderer)
|
||||
|
||||
MetavoxelSystem::NetworkSimulation::NetworkSimulation(float dropRate, float repeatRate,
|
||||
int minimumDelay, int maximumDelay, int bandwidthLimit) :
|
||||
|
@ -2854,6 +2855,7 @@ ProgramObject DefaultMetavoxelRendererImplementation::_voxelCursorProgram;
|
|||
SphereRenderer::SphereRenderer() {
|
||||
}
|
||||
|
||||
|
||||
void SphereRenderer::render(const glm::vec4& color, Mode mode) {
|
||||
Sphere* sphere = static_cast<Sphere*>(_spanner);
|
||||
const QColor& ownColor = sphere->getColor();
|
||||
|
@ -2969,3 +2971,9 @@ void StaticModelRenderer::applyScale(float scale) {
|
|||
void StaticModelRenderer::applyURL(const QUrl& url) {
|
||||
_model->setURL(url);
|
||||
}
|
||||
|
||||
HeightfieldRenderer::HeightfieldRenderer() {
|
||||
}
|
||||
|
||||
void HeightfieldRenderer::render(const glm::vec4& color, Mode mode) {
|
||||
}
|
||||
|
|
|
@ -450,4 +450,15 @@ private:
|
|||
Model* _model;
|
||||
};
|
||||
|
||||
/// Renders heightfields.
|
||||
class HeightfieldRenderer : public SpannerRenderer {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
Q_INVOKABLE HeightfieldRenderer();
|
||||
|
||||
virtual void render(const glm::vec4& color, Mode mode);
|
||||
};
|
||||
|
||||
#endif // hifi_MetavoxelSystem_h
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include "AttributeRegistry.h"
|
||||
#include "MetavoxelData.h"
|
||||
#include "Spanner.h"
|
||||
|
||||
REGISTER_META_OBJECT(FloatAttribute)
|
||||
REGISTER_META_OBJECT(MaterialObject)
|
||||
|
@ -54,6 +55,7 @@ AttributeRegistry::AttributeRegistry() :
|
|||
// our baseline LOD threshold is for voxels; spanners and heightfields are a different story
|
||||
const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 8.0f;
|
||||
_spannersAttribute->setLODThresholdMultiplier(SPANNER_LOD_THRESHOLD_MULTIPLIER);
|
||||
_spannersAttribute->setUserFacing(true);
|
||||
|
||||
const float HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER = 32.0f;
|
||||
_heightfieldAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "DatagramSequencer.h"
|
||||
#include "MetavoxelData.h"
|
||||
#include "Spanner.h"
|
||||
|
||||
class PacketRecord;
|
||||
|
||||
|
|
|
@ -11,24 +11,15 @@
|
|||
|
||||
#include <QDateTime>
|
||||
#include <QDebugStateSaver>
|
||||
#include <QThread>
|
||||
#include <QtDebug>
|
||||
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include <GeometryUtil.h>
|
||||
|
||||
#include "MetavoxelData.h"
|
||||
#include "MetavoxelUtil.h"
|
||||
#include "Spanner.h"
|
||||
|
||||
REGISTER_META_OBJECT(MetavoxelGuide)
|
||||
REGISTER_META_OBJECT(DefaultMetavoxelGuide)
|
||||
REGISTER_META_OBJECT(MetavoxelRenderer)
|
||||
REGISTER_META_OBJECT(DefaultMetavoxelRenderer)
|
||||
REGISTER_META_OBJECT(Spanner)
|
||||
REGISTER_META_OBJECT(Sphere)
|
||||
REGISTER_META_OBJECT(Cuboid)
|
||||
REGISTER_META_OBJECT(StaticModel)
|
||||
|
||||
static int metavoxelDataTypeId = registerSimpleMetaType<MetavoxelData>();
|
||||
|
||||
|
@ -1800,549 +1791,3 @@ QByteArray DefaultMetavoxelRenderer::getImplementationClassName() const {
|
|||
return "DefaultMetavoxelRendererImplementation";
|
||||
}
|
||||
|
||||
const float DEFAULT_PLACEMENT_GRANULARITY = 0.01f;
|
||||
const float DEFAULT_VOXELIZATION_GRANULARITY = powf(2.0f, -3.0f);
|
||||
|
||||
Spanner::Spanner() :
|
||||
_renderer(NULL),
|
||||
_placementGranularity(DEFAULT_PLACEMENT_GRANULARITY),
|
||||
_voxelizationGranularity(DEFAULT_VOXELIZATION_GRANULARITY),
|
||||
_merged(false) {
|
||||
}
|
||||
|
||||
void Spanner::setBounds(const Box& bounds) {
|
||||
if (_bounds == bounds) {
|
||||
return;
|
||||
}
|
||||
emit boundsWillChange();
|
||||
emit boundsChanged(_bounds = bounds);
|
||||
}
|
||||
|
||||
bool Spanner::testAndSetVisited(int visit) {
|
||||
QMutexLocker locker(&_lastVisitsMutex);
|
||||
int& lastVisit = _lastVisits[QThread::currentThread()];
|
||||
if (lastVisit == visit) {
|
||||
return false;
|
||||
}
|
||||
lastVisit = visit;
|
||||
return true;
|
||||
}
|
||||
|
||||
SpannerRenderer* Spanner::getRenderer() {
|
||||
if (!_renderer) {
|
||||
QByteArray className = getRendererClassName();
|
||||
const QMetaObject* metaObject = Bitstream::getMetaObject(className);
|
||||
if (!metaObject) {
|
||||
qDebug() << "Unknown class name:" << className;
|
||||
metaObject = &SpannerRenderer::staticMetaObject;
|
||||
}
|
||||
_renderer = static_cast<SpannerRenderer*>(metaObject->newInstance());
|
||||
connect(this, &QObject::destroyed, _renderer, &QObject::deleteLater);
|
||||
_renderer->init(this);
|
||||
}
|
||||
return _renderer;
|
||||
}
|
||||
|
||||
bool Spanner::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
|
||||
return _bounds.findRayIntersection(origin, direction, distance);
|
||||
}
|
||||
|
||||
bool Spanner::hasOwnColors() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Spanner::hasOwnMaterials() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
QRgb Spanner::getColorAt(const glm::vec3& point) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Spanner::getMaterialAt(const glm::vec3& point) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
QVector<SharedObjectPointer>& Spanner::getMaterials() {
|
||||
static QVector<SharedObjectPointer> emptyMaterials;
|
||||
return emptyMaterials;
|
||||
}
|
||||
|
||||
bool Spanner::contains(const glm::vec3& point) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Spanner::intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray Spanner::getRendererClassName() const {
|
||||
return "SpannerRendererer";
|
||||
}
|
||||
|
||||
QAtomicInt Spanner::_nextVisit(1);
|
||||
|
||||
SpannerRenderer::SpannerRenderer() {
|
||||
}
|
||||
|
||||
void SpannerRenderer::init(Spanner* spanner) {
|
||||
_spanner = spanner;
|
||||
}
|
||||
|
||||
void SpannerRenderer::simulate(float deltaTime) {
|
||||
// nothing by default
|
||||
}
|
||||
|
||||
void SpannerRenderer::render(const glm::vec4& color, Mode mode) {
|
||||
// nothing by default
|
||||
}
|
||||
|
||||
bool SpannerRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
Transformable::Transformable() : _scale(1.0f) {
|
||||
}
|
||||
|
||||
void Transformable::setTranslation(const glm::vec3& translation) {
|
||||
if (_translation != translation) {
|
||||
emit translationChanged(_translation = translation);
|
||||
}
|
||||
}
|
||||
|
||||
void Transformable::setRotation(const glm::quat& rotation) {
|
||||
if (_rotation != rotation) {
|
||||
emit rotationChanged(_rotation = rotation);
|
||||
}
|
||||
}
|
||||
|
||||
void Transformable::setScale(float scale) {
|
||||
if (_scale != scale) {
|
||||
emit scaleChanged(_scale = scale);
|
||||
}
|
||||
}
|
||||
|
||||
ColorTransformable::ColorTransformable() :
|
||||
_color(Qt::white) {
|
||||
}
|
||||
|
||||
void ColorTransformable::setColor(const QColor& color) {
|
||||
if (_color != color) {
|
||||
emit colorChanged(_color = color);
|
||||
}
|
||||
}
|
||||
|
||||
Sphere::Sphere() {
|
||||
connect(this, SIGNAL(translationChanged(const glm::vec3&)), SLOT(updateBounds()));
|
||||
connect(this, SIGNAL(scaleChanged(float)), SLOT(updateBounds()));
|
||||
updateBounds();
|
||||
}
|
||||
|
||||
bool Sphere::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
|
||||
return findRaySphereIntersection(origin, direction, getTranslation(), getScale(), distance);
|
||||
}
|
||||
|
||||
bool Sphere::contains(const glm::vec3& point) {
|
||||
return glm::distance(point, getTranslation()) <= getScale();
|
||||
}
|
||||
|
||||
bool Sphere::intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal) {
|
||||
glm::vec3 relativeStart = start - getTranslation();
|
||||
glm::vec3 vector = end - start;
|
||||
float a = glm::dot(vector, vector);
|
||||
if (a == 0.0f) {
|
||||
return false;
|
||||
}
|
||||
float b = glm::dot(relativeStart, vector);
|
||||
float radicand = b * b - a * (glm::dot(relativeStart, relativeStart) - getScale() * getScale());
|
||||
if (radicand < 0.0f) {
|
||||
return false;
|
||||
}
|
||||
float radical = glm::sqrt(radicand);
|
||||
float first = (-b - radical) / a;
|
||||
if (first >= 0.0f && first <= 1.0f) {
|
||||
distance = first;
|
||||
normal = glm::normalize(relativeStart + vector * distance);
|
||||
return true;
|
||||
}
|
||||
float second = (-b + radical) / a;
|
||||
if (second >= 0.0f && second <= 1.0f) {
|
||||
distance = second;
|
||||
normal = glm::normalize(relativeStart + vector * distance);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray Sphere::getRendererClassName() const {
|
||||
return "SphereRenderer";
|
||||
}
|
||||
|
||||
void Sphere::updateBounds() {
|
||||
glm::vec3 extent(getScale(), getScale(), getScale());
|
||||
setBounds(Box(getTranslation() - extent, getTranslation() + extent));
|
||||
}
|
||||
|
||||
Cuboid::Cuboid() :
|
||||
_aspectY(1.0f),
|
||||
_aspectZ(1.0f) {
|
||||
|
||||
connect(this, &Cuboid::translationChanged, this, &Cuboid::updateBoundsAndPlanes);
|
||||
connect(this, &Cuboid::rotationChanged, this, &Cuboid::updateBoundsAndPlanes);
|
||||
connect(this, &Cuboid::scaleChanged, this, &Cuboid::updateBoundsAndPlanes);
|
||||
connect(this, &Cuboid::aspectYChanged, this, &Cuboid::updateBoundsAndPlanes);
|
||||
connect(this, &Cuboid::aspectZChanged, this, &Cuboid::updateBoundsAndPlanes);
|
||||
updateBoundsAndPlanes();
|
||||
}
|
||||
|
||||
void Cuboid::setAspectY(float aspectY) {
|
||||
if (_aspectY != aspectY) {
|
||||
emit aspectYChanged(_aspectY = aspectY);
|
||||
}
|
||||
}
|
||||
|
||||
void Cuboid::setAspectZ(float aspectZ) {
|
||||
if (_aspectZ != aspectZ) {
|
||||
emit aspectZChanged(_aspectZ = aspectZ);
|
||||
}
|
||||
}
|
||||
|
||||
bool Cuboid::contains(const glm::vec3& point) {
|
||||
glm::vec4 point4(point, 1.0f);
|
||||
for (int i = 0; i < PLANE_COUNT; i++) {
|
||||
if (glm::dot(_planes[i], point4) > 0.0f) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Cuboid::intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal) {
|
||||
glm::vec4 start4(start, 1.0f);
|
||||
glm::vec4 vector = glm::vec4(end - start, 0.0f);
|
||||
for (int i = 0; i < PLANE_COUNT; i++) {
|
||||
// first check the segment against the plane
|
||||
float divisor = glm::dot(_planes[i], vector);
|
||||
if (glm::abs(divisor) < EPSILON) {
|
||||
continue;
|
||||
}
|
||||
float t = -glm::dot(_planes[i], start4) / divisor;
|
||||
if (t < 0.0f || t > 1.0f) {
|
||||
continue;
|
||||
}
|
||||
// now that we've established that it intersects the plane, check against the other sides
|
||||
glm::vec4 point = start4 + vector * t;
|
||||
const int PLANES_PER_AXIS = 2;
|
||||
int indexOffset = ((i / PLANES_PER_AXIS) + 1) * PLANES_PER_AXIS;
|
||||
for (int j = 0; j < PLANE_COUNT - PLANES_PER_AXIS; j++) {
|
||||
if (glm::dot(_planes[(indexOffset + j) % PLANE_COUNT], point) > 0.0f) {
|
||||
goto outerContinue;
|
||||
}
|
||||
}
|
||||
distance = t;
|
||||
normal = glm::vec3(_planes[i]);
|
||||
return true;
|
||||
|
||||
outerContinue: ;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray Cuboid::getRendererClassName() const {
|
||||
return "CuboidRenderer";
|
||||
}
|
||||
|
||||
void Cuboid::updateBoundsAndPlanes() {
|
||||
glm::vec3 extent(getScale(), getScale() * _aspectY, getScale() * _aspectZ);
|
||||
glm::mat4 rotationMatrix = glm::mat4_cast(getRotation());
|
||||
setBounds(glm::translate(getTranslation()) * rotationMatrix * Box(-extent, extent));
|
||||
|
||||
glm::vec4 translation4 = glm::vec4(getTranslation(), 1.0f);
|
||||
_planes[0] = glm::vec4(glm::vec3(rotationMatrix[0]), -glm::dot(rotationMatrix[0], translation4) - getScale());
|
||||
_planes[1] = glm::vec4(glm::vec3(-rotationMatrix[0]), glm::dot(rotationMatrix[0], translation4) - getScale());
|
||||
_planes[2] = glm::vec4(glm::vec3(rotationMatrix[1]), -glm::dot(rotationMatrix[1], translation4) - getScale() * _aspectY);
|
||||
_planes[3] = glm::vec4(glm::vec3(-rotationMatrix[1]), glm::dot(rotationMatrix[1], translation4) - getScale() * _aspectY);
|
||||
_planes[4] = glm::vec4(glm::vec3(rotationMatrix[2]), -glm::dot(rotationMatrix[2], translation4) - getScale() * _aspectZ);
|
||||
_planes[5] = glm::vec4(glm::vec3(-rotationMatrix[2]), glm::dot(rotationMatrix[2], translation4) - getScale() * _aspectZ);
|
||||
}
|
||||
|
||||
StaticModel::StaticModel() {
|
||||
}
|
||||
|
||||
void StaticModel::setURL(const QUrl& url) {
|
||||
if (_url != url) {
|
||||
emit urlChanged(_url = url);
|
||||
}
|
||||
}
|
||||
|
||||
bool StaticModel::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
|
||||
// delegate to renderer, if we have one
|
||||
return _renderer ? _renderer->findRayIntersection(origin, direction, distance) :
|
||||
Spanner::findRayIntersection(origin, direction, distance);
|
||||
}
|
||||
|
||||
QByteArray StaticModel::getRendererClassName() const {
|
||||
return "StaticModelRenderer";
|
||||
}
|
||||
|
||||
const float EIGHT_BIT_MAXIMUM = 255.0f;
|
||||
|
||||
Heightfield::Heightfield(const Box& bounds, float increment, const QByteArray& height, const QByteArray& color,
|
||||
const QByteArray& material, const QVector<SharedObjectPointer>& materials) :
|
||||
_increment(increment),
|
||||
_width((int)glm::round((bounds.maximum.x - bounds.minimum.x) / increment) + 1),
|
||||
_heightScale((bounds.maximum.y - bounds.minimum.y) / EIGHT_BIT_MAXIMUM),
|
||||
_height(height),
|
||||
_color(color),
|
||||
_material(material),
|
||||
_materials(materials) {
|
||||
|
||||
setBounds(bounds);
|
||||
}
|
||||
|
||||
bool Heightfield::hasOwnColors() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Heightfield::hasOwnMaterials() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
QRgb Heightfield::getColorAt(const glm::vec3& point) {
|
||||
glm::vec3 relative = (point - getBounds().minimum) / _increment;
|
||||
glm::vec3 floors = glm::floor(relative);
|
||||
glm::vec3 ceils = glm::ceil(relative);
|
||||
glm::vec3 fracts = glm::fract(relative);
|
||||
int floorX = (int)floors.x;
|
||||
int floorZ = (int)floors.z;
|
||||
int ceilX = (int)ceils.x;
|
||||
int ceilZ = (int)ceils.z;
|
||||
const uchar* src = (const uchar*)_color.constData();
|
||||
const uchar* upperLeft = src + (floorZ * _width + floorX) * DataBlock::COLOR_BYTES;
|
||||
const uchar* lowerRight = src + (ceilZ * _width + ceilX) * DataBlock::COLOR_BYTES;
|
||||
glm::vec3 interpolatedColor = glm::mix(glm::vec3(upperLeft[0], upperLeft[1], upperLeft[2]),
|
||||
glm::vec3(lowerRight[0], lowerRight[1], lowerRight[2]), fracts.z);
|
||||
|
||||
// the final vertex (and thus which triangle we check) depends on which half we're on
|
||||
if (fracts.x >= fracts.z) {
|
||||
const uchar* upperRight = src + (floorZ * _width + ceilX) * DataBlock::COLOR_BYTES;
|
||||
interpolatedColor = glm::mix(interpolatedColor, glm::mix(glm::vec3(upperRight[0], upperRight[1], upperRight[2]),
|
||||
glm::vec3(lowerRight[0], lowerRight[1], lowerRight[2]), fracts.z), (fracts.x - fracts.z) / (1.0f - fracts.z));
|
||||
|
||||
} else {
|
||||
const uchar* lowerLeft = src + (ceilZ * _width + floorX) * DataBlock::COLOR_BYTES;
|
||||
interpolatedColor = glm::mix(glm::mix(glm::vec3(upperLeft[0], upperLeft[1], upperLeft[2]),
|
||||
glm::vec3(lowerLeft[0], lowerLeft[1], lowerLeft[2]), fracts.z), interpolatedColor, fracts.x / fracts.z);
|
||||
}
|
||||
return qRgb(interpolatedColor.r, interpolatedColor.g, interpolatedColor.b);
|
||||
}
|
||||
|
||||
int Heightfield::getMaterialAt(const glm::vec3& point) {
|
||||
glm::vec3 relative = (point - getBounds().minimum) / _increment;
|
||||
const uchar* src = (const uchar*)_material.constData();
|
||||
return src[(int)glm::round(relative.z) * _width + (int)glm::round(relative.x)];
|
||||
}
|
||||
|
||||
QVector<SharedObjectPointer>& Heightfield::getMaterials() {
|
||||
return _materials;
|
||||
}
|
||||
|
||||
bool Heightfield::contains(const glm::vec3& point) {
|
||||
if (!getBounds().contains(point)) {
|
||||
return false;
|
||||
}
|
||||
glm::vec3 relative = (point - getBounds().minimum) / _increment;
|
||||
glm::vec3 floors = glm::floor(relative);
|
||||
glm::vec3 ceils = glm::ceil(relative);
|
||||
glm::vec3 fracts = glm::fract(relative);
|
||||
int floorX = (int)floors.x;
|
||||
int floorZ = (int)floors.z;
|
||||
int ceilX = (int)ceils.x;
|
||||
int ceilZ = (int)ceils.z;
|
||||
const uchar* src = (const uchar*)_height.constData();
|
||||
float upperLeft = src[floorZ * _width + floorX];
|
||||
float lowerRight = src[ceilZ * _width + ceilX];
|
||||
float interpolatedHeight = glm::mix(upperLeft, lowerRight, fracts.z);
|
||||
|
||||
// the final vertex (and thus which triangle we check) depends on which half we're on
|
||||
if (fracts.x >= fracts.z) {
|
||||
float upperRight = src[floorZ * _width + ceilX];
|
||||
interpolatedHeight = glm::mix(interpolatedHeight, glm::mix(upperRight, lowerRight, fracts.z),
|
||||
(fracts.x - fracts.z) / (1.0f - fracts.z));
|
||||
|
||||
} else {
|
||||
float lowerLeft = src[ceilZ * _width + floorX];
|
||||
interpolatedHeight = glm::mix(glm::mix(upperLeft, lowerLeft, fracts.z), interpolatedHeight, fracts.x / fracts.z);
|
||||
}
|
||||
return interpolatedHeight != 0.0f && point.y <= interpolatedHeight * _heightScale + getBounds().minimum.y;
|
||||
}
|
||||
|
||||
bool Heightfield::intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal) {
|
||||
// find the initial location in heightfield coordinates
|
||||
float rayDistance;
|
||||
glm::vec3 direction = end - start;
|
||||
if (!getBounds().findRayIntersection(start, direction, rayDistance) || rayDistance > 1.0f) {
|
||||
return false;
|
||||
}
|
||||
glm::vec3 entry = start + direction * rayDistance;
|
||||
const float DISTANCE_THRESHOLD = 0.001f;
|
||||
if (glm::abs(entry.x - getBounds().minimum.x) < DISTANCE_THRESHOLD) {
|
||||
normal = glm::vec3(-1.0f, 0.0f, 0.0f);
|
||||
distance = rayDistance;
|
||||
return true;
|
||||
|
||||
} else if (glm::abs(entry.x - getBounds().maximum.x) < DISTANCE_THRESHOLD) {
|
||||
normal = glm::vec3(1.0f, 0.0f, 0.0f);
|
||||
distance = rayDistance;
|
||||
return true;
|
||||
|
||||
} else if (glm::abs(entry.y - getBounds().minimum.y) < DISTANCE_THRESHOLD) {
|
||||
normal = glm::vec3(0.0f, -1.0f, 0.0f);
|
||||
distance = rayDistance;
|
||||
return true;
|
||||
|
||||
} else if (glm::abs(entry.y - getBounds().maximum.y) < DISTANCE_THRESHOLD) {
|
||||
normal = glm::vec3(0.0f, 1.0f, 0.0f);
|
||||
distance = rayDistance;
|
||||
return true;
|
||||
|
||||
} else if (glm::abs(entry.z - getBounds().minimum.z) < DISTANCE_THRESHOLD) {
|
||||
normal = glm::vec3(0.0f, 0.0f, -1.0f);
|
||||
distance = rayDistance;
|
||||
return true;
|
||||
|
||||
} else if (glm::abs(entry.z - getBounds().maximum.z) < DISTANCE_THRESHOLD) {
|
||||
normal = glm::vec3(0.0f, 0.0f, 1.0f);
|
||||
distance = rayDistance;
|
||||
return true;
|
||||
}
|
||||
entry = (entry - getBounds().minimum) / _increment;
|
||||
glm::vec3 floors = glm::floor(entry);
|
||||
glm::vec3 ceils = glm::ceil(entry);
|
||||
if (floors.x == ceils.x) {
|
||||
if (direction.x > 0.0f) {
|
||||
ceils.x += 1.0f;
|
||||
} else {
|
||||
floors.x -= 1.0f;
|
||||
}
|
||||
}
|
||||
if (floors.z == ceils.z) {
|
||||
if (direction.z > 0.0f) {
|
||||
ceils.z += 1.0f;
|
||||
} else {
|
||||
floors.z -= 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
bool withinBounds = true;
|
||||
float accumulatedDistance = 0.0f;
|
||||
const uchar* src = (const uchar*)_height.constData();
|
||||
int highestX = _width - 1;
|
||||
float highestY = (getBounds().maximum.y - getBounds().minimum.y) / _increment;
|
||||
int highestZ = (int)glm::round((getBounds().maximum.z - getBounds().minimum.z) / _increment);
|
||||
float heightScale = _heightScale / _increment;
|
||||
while (withinBounds && accumulatedDistance <= 1.0f) {
|
||||
// find the heights at the corners of the current cell
|
||||
int floorX = qMin(qMax((int)floors.x, 0), highestX);
|
||||
int floorZ = qMin(qMax((int)floors.z, 0), highestZ);
|
||||
int ceilX = qMin(qMax((int)ceils.x, 0), highestX);
|
||||
int ceilZ = qMin(qMax((int)ceils.z, 0), highestZ);
|
||||
float upperLeft = src[floorZ * _width + floorX] * heightScale;
|
||||
float upperRight = src[floorZ * _width + ceilX] * heightScale;
|
||||
float lowerLeft = src[ceilZ * _width + floorX] * heightScale;
|
||||
float lowerRight = src[ceilZ * _width + ceilX] * heightScale;
|
||||
|
||||
// find the distance to the next x coordinate
|
||||
float xDistance = FLT_MAX;
|
||||
if (direction.x > 0.0f) {
|
||||
xDistance = (ceils.x - entry.x) / direction.x;
|
||||
} else if (direction.x < 0.0f) {
|
||||
xDistance = (floors.x - entry.x) / direction.x;
|
||||
}
|
||||
|
||||
// and the distance to the next z coordinate
|
||||
float zDistance = FLT_MAX;
|
||||
if (direction.z > 0.0f) {
|
||||
zDistance = (ceils.z - entry.z) / direction.z;
|
||||
} else if (direction.z < 0.0f) {
|
||||
zDistance = (floors.z - entry.z) / direction.z;
|
||||
}
|
||||
|
||||
// the exit distance is the lower of those two
|
||||
float exitDistance = qMin(xDistance, zDistance);
|
||||
glm::vec3 exit, nextFloors = floors, nextCeils = ceils;
|
||||
if (exitDistance == FLT_MAX) {
|
||||
withinBounds = false; // line points upwards/downwards; check this cell only
|
||||
|
||||
} else {
|
||||
// find the exit point and the next cell, and determine whether it's still within the bounds
|
||||
exit = entry + exitDistance * direction;
|
||||
withinBounds = (exit.y >= 0.0f && exit.y <= highestY);
|
||||
if (exitDistance == xDistance) {
|
||||
if (direction.x > 0.0f) {
|
||||
nextFloors.x += 1.0f;
|
||||
withinBounds &= (nextCeils.x += 1.0f) <= highestX;
|
||||
} else {
|
||||
withinBounds &= (nextFloors.x -= 1.0f) >= 0.0f;
|
||||
nextCeils.x -= 1.0f;
|
||||
}
|
||||
}
|
||||
if (exitDistance == zDistance) {
|
||||
if (direction.z > 0.0f) {
|
||||
nextFloors.z += 1.0f;
|
||||
withinBounds &= (nextCeils.z += 1.0f) <= highestZ;
|
||||
} else {
|
||||
withinBounds &= (nextFloors.z -= 1.0f) >= 0.0f;
|
||||
nextCeils.z -= 1.0f;
|
||||
}
|
||||
}
|
||||
// check the vertical range of the ray against the ranges of the cell heights
|
||||
if (qMin(entry.y, exit.y) > qMax(qMax(upperLeft, upperRight), qMax(lowerLeft, lowerRight)) ||
|
||||
qMax(entry.y, exit.y) < qMin(qMin(upperLeft, upperRight), qMin(lowerLeft, lowerRight))) {
|
||||
entry = exit;
|
||||
floors = nextFloors;
|
||||
ceils = nextCeils;
|
||||
accumulatedDistance += exitDistance;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// having passed the bounds check, we must check against the planes
|
||||
glm::vec3 relativeEntry = entry - glm::vec3(floors.x, upperLeft, floors.z);
|
||||
|
||||
// first check the triangle including the Z+ segment
|
||||
glm::vec3 lowerNormal(lowerLeft - lowerRight, 1.0f, upperLeft - lowerLeft);
|
||||
float lowerProduct = glm::dot(lowerNormal, direction);
|
||||
if (lowerProduct != 0.0f) {
|
||||
float planeDistance = -glm::dot(lowerNormal, relativeEntry) / lowerProduct;
|
||||
glm::vec3 intersection = relativeEntry + planeDistance * direction;
|
||||
if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f &&
|
||||
intersection.z >= intersection.x) {
|
||||
distance = rayDistance + (accumulatedDistance + planeDistance) * _increment;
|
||||
normal = glm::normalize(lowerNormal);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// then the one with the X+ segment
|
||||
glm::vec3 upperNormal(upperLeft - upperRight, 1.0f, upperRight - lowerRight);
|
||||
float upperProduct = glm::dot(upperNormal, direction);
|
||||
if (upperProduct != 0.0f) {
|
||||
float planeDistance = -glm::dot(upperNormal, relativeEntry) / upperProduct;
|
||||
glm::vec3 intersection = relativeEntry + planeDistance * direction;
|
||||
if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f &&
|
||||
intersection.x >= intersection.z) {
|
||||
distance = rayDistance + (accumulatedDistance + planeDistance) * _increment;
|
||||
normal = glm::normalize(upperNormal);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// no joy; continue on our way
|
||||
entry = exit;
|
||||
floors = nextFloors;
|
||||
ceils = nextCeils;
|
||||
accumulatedDistance += exitDistance;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -28,9 +28,7 @@ class MetavoxelNode;
|
|||
class MetavoxelRendererImplementation;
|
||||
class MetavoxelVisitation;
|
||||
class MetavoxelVisitor;
|
||||
class NetworkValue;
|
||||
class Spanner;
|
||||
class SpannerRenderer;
|
||||
|
||||
/// Determines whether to subdivide each node when traversing. Contains the position (presumed to be of the viewer) and a
|
||||
/// threshold value, where lower thresholds cause smaller/more distant voxels to be subdivided.
|
||||
|
@ -526,281 +524,4 @@ public:
|
|||
virtual QByteArray getImplementationClassName() const;
|
||||
};
|
||||
|
||||
/// An object that spans multiple octree cells.
|
||||
class Spanner : public SharedObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(Box bounds MEMBER _bounds WRITE setBounds NOTIFY boundsChanged DESIGNABLE false)
|
||||
Q_PROPERTY(float placementGranularity MEMBER _placementGranularity DESIGNABLE false)
|
||||
Q_PROPERTY(float voxelizationGranularity MEMBER _voxelizationGranularity DESIGNABLE false)
|
||||
|
||||
public:
|
||||
|
||||
/// Returns the value of the global visit counter and increments it.
|
||||
static int getAndIncrementNextVisit() { return _nextVisit.fetchAndAddOrdered(1); }
|
||||
|
||||
Spanner();
|
||||
|
||||
void setBounds(const Box& bounds);
|
||||
const Box& getBounds() const { return _bounds; }
|
||||
|
||||
void setPlacementGranularity(float granularity) { _placementGranularity = granularity; }
|
||||
float getPlacementGranularity() const { return _placementGranularity; }
|
||||
|
||||
void setVoxelizationGranularity(float granularity) { _voxelizationGranularity = granularity; }
|
||||
float getVoxelizationGranularity() const { return _voxelizationGranularity; }
|
||||
|
||||
void setMerged(bool merged) { _merged = merged; }
|
||||
bool isMerged() const { return _merged; }
|
||||
|
||||
/// Checks whether we've visited this object on the current traversal. If we have, returns false.
|
||||
/// If we haven't, sets the last visit identifier and returns true.
|
||||
bool testAndSetVisited(int visit);
|
||||
|
||||
/// Returns a pointer to the renderer, creating it if necessary.
|
||||
SpannerRenderer* getRenderer();
|
||||
|
||||
/// Finds the intersection between the described ray and this spanner.
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
|
||||
|
||||
/// Checks whether this spanner has its own colors.
|
||||
virtual bool hasOwnColors() const;
|
||||
|
||||
/// Checks whether this spanner has its own materials.
|
||||
virtual bool hasOwnMaterials() const;
|
||||
|
||||
/// Checks whether the spanner contains the specified point.
|
||||
virtual bool contains(const glm::vec3& point);
|
||||
|
||||
/// Retrieves the color at the specified point.
|
||||
virtual QRgb getColorAt(const glm::vec3& point);
|
||||
|
||||
/// Retrieves the material at the specified point.
|
||||
virtual int getMaterialAt(const glm::vec3& point);
|
||||
|
||||
/// Retrieves a reference to the list of materials.
|
||||
virtual QVector<SharedObjectPointer>& getMaterials();
|
||||
|
||||
/// Finds the intersection, if any, between the specified line segment and the spanner.
|
||||
virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal);
|
||||
|
||||
signals:
|
||||
|
||||
void boundsWillChange();
|
||||
void boundsChanged(const Box& bounds);
|
||||
|
||||
protected:
|
||||
|
||||
SpannerRenderer* _renderer;
|
||||
|
||||
/// Returns the name of the class to instantiate in order to render this spanner.
|
||||
virtual QByteArray getRendererClassName() const;
|
||||
|
||||
private:
|
||||
|
||||
Box _bounds;
|
||||
float _placementGranularity;
|
||||
float _voxelizationGranularity;
|
||||
bool _merged;
|
||||
QHash<QThread*, int> _lastVisits; ///< last visit identifiers for each thread
|
||||
QMutex _lastVisitsMutex;
|
||||
|
||||
static QAtomicInt _nextVisit; ///< the global visit counter
|
||||
};
|
||||
|
||||
/// Base class for objects that can render spanners.
|
||||
class SpannerRenderer : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
enum Mode { DEFAULT_MODE, DIFFUSE_MODE, NORMAL_MODE };
|
||||
|
||||
Q_INVOKABLE SpannerRenderer();
|
||||
|
||||
virtual void init(Spanner* spanner);
|
||||
virtual void simulate(float deltaTime);
|
||||
virtual void render(const glm::vec4& color, Mode mode);
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
|
||||
|
||||
protected:
|
||||
|
||||
Spanner* _spanner;
|
||||
};
|
||||
|
||||
/// An object with a 3D transform.
|
||||
class Transformable : public Spanner {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(glm::vec3 translation MEMBER _translation WRITE setTranslation NOTIFY translationChanged)
|
||||
Q_PROPERTY(glm::quat rotation MEMBER _rotation WRITE setRotation NOTIFY rotationChanged)
|
||||
Q_PROPERTY(float scale MEMBER _scale WRITE setScale NOTIFY scaleChanged)
|
||||
|
||||
public:
|
||||
|
||||
Transformable();
|
||||
|
||||
void setTranslation(const glm::vec3& translation);
|
||||
const glm::vec3& getTranslation() const { return _translation; }
|
||||
|
||||
void setRotation(const glm::quat& rotation);
|
||||
const glm::quat& getRotation() const { return _rotation; }
|
||||
|
||||
void setScale(float scale);
|
||||
float getScale() const { return _scale; }
|
||||
|
||||
signals:
|
||||
|
||||
void translationChanged(const glm::vec3& translation);
|
||||
void rotationChanged(const glm::quat& rotation);
|
||||
void scaleChanged(float scale);
|
||||
|
||||
private:
|
||||
|
||||
glm::vec3 _translation;
|
||||
glm::quat _rotation;
|
||||
float _scale;
|
||||
};
|
||||
|
||||
/// A transformable object with a color.
|
||||
class ColorTransformable : public Transformable {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QColor color MEMBER _color WRITE setColor NOTIFY colorChanged DESIGNABLE false)
|
||||
|
||||
public:
|
||||
|
||||
ColorTransformable();
|
||||
|
||||
void setColor(const QColor& color);
|
||||
const QColor& getColor() const { return _color; }
|
||||
|
||||
signals:
|
||||
|
||||
void colorChanged(const QColor& color);
|
||||
|
||||
protected:
|
||||
|
||||
QColor _color;
|
||||
};
|
||||
|
||||
/// A sphere.
|
||||
class Sphere : public ColorTransformable {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
Q_INVOKABLE Sphere();
|
||||
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
|
||||
virtual bool contains(const glm::vec3& point);
|
||||
virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal);
|
||||
|
||||
protected:
|
||||
|
||||
virtual QByteArray getRendererClassName() const;
|
||||
|
||||
private slots:
|
||||
|
||||
void updateBounds();
|
||||
};
|
||||
|
||||
/// A cuboid.
|
||||
class Cuboid : public ColorTransformable {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(float aspectY MEMBER _aspectY WRITE setAspectY NOTIFY aspectYChanged)
|
||||
Q_PROPERTY(float aspectZ MEMBER _aspectZ WRITE setAspectZ NOTIFY aspectZChanged)
|
||||
|
||||
public:
|
||||
|
||||
Q_INVOKABLE Cuboid();
|
||||
|
||||
void setAspectY(float aspectY);
|
||||
float getAspectY() const { return _aspectY; }
|
||||
|
||||
void setAspectZ(float aspectZ);
|
||||
float getAspectZ() const { return _aspectZ; }
|
||||
|
||||
virtual bool contains(const glm::vec3& point);
|
||||
virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal);
|
||||
|
||||
signals:
|
||||
|
||||
void aspectYChanged(float aspectY);
|
||||
void aspectZChanged(float aspectZ);
|
||||
|
||||
protected:
|
||||
|
||||
virtual QByteArray getRendererClassName() const;
|
||||
|
||||
private slots:
|
||||
|
||||
void updateBoundsAndPlanes();
|
||||
|
||||
private:
|
||||
|
||||
float _aspectY;
|
||||
float _aspectZ;
|
||||
|
||||
static const int PLANE_COUNT = 6;
|
||||
glm::vec4 _planes[PLANE_COUNT];
|
||||
};
|
||||
|
||||
/// A static 3D model loaded from the network.
|
||||
class StaticModel : public Transformable {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QUrl url MEMBER _url WRITE setURL NOTIFY urlChanged)
|
||||
|
||||
public:
|
||||
|
||||
Q_INVOKABLE StaticModel();
|
||||
|
||||
void setURL(const QUrl& url);
|
||||
const QUrl& getURL() const { return _url; }
|
||||
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
|
||||
|
||||
signals:
|
||||
|
||||
void urlChanged(const QUrl& url);
|
||||
|
||||
protected:
|
||||
|
||||
virtual QByteArray getRendererClassName() const;
|
||||
|
||||
private:
|
||||
|
||||
QUrl _url;
|
||||
};
|
||||
|
||||
/// A heightfield represented as a spanner.
|
||||
class Heightfield : public Transformable {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
Heightfield(const Box& bounds, float increment, const QByteArray& height, const QByteArray& color,
|
||||
const QByteArray& material, const QVector<SharedObjectPointer>& materials);
|
||||
|
||||
QByteArray& getHeight() { return _height; }
|
||||
QByteArray& getColor() { return _color; }
|
||||
QByteArray& getMaterial() { return _material; }
|
||||
|
||||
virtual bool hasOwnColors() const;
|
||||
virtual bool hasOwnMaterials() const;
|
||||
virtual QRgb getColorAt(const glm::vec3& point);
|
||||
virtual int getMaterialAt(const glm::vec3& point);
|
||||
virtual QVector<SharedObjectPointer>& getMaterials();
|
||||
|
||||
virtual bool contains(const glm::vec3& point);
|
||||
virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal);
|
||||
|
||||
private:
|
||||
|
||||
float _increment;
|
||||
int _width;
|
||||
float _heightScale;
|
||||
QByteArray _height;
|
||||
QByteArray _color;
|
||||
QByteArray _material;
|
||||
QVector<SharedObjectPointer> _materials;
|
||||
};
|
||||
|
||||
#endif // hifi_MetavoxelData_h
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
//
|
||||
|
||||
#include "MetavoxelMessages.h"
|
||||
#include "Spanner.h"
|
||||
|
||||
void MetavoxelEditMessage::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
|
||||
static_cast<const MetavoxelEdit*>(edit.data())->apply(data, objects);
|
||||
|
@ -754,7 +755,7 @@ int HeightfieldClearFetchVisitor::visit(MetavoxelInfo& info) {
|
|||
}
|
||||
|
||||
// create spanner if necessary
|
||||
Heightfield* spanner = static_cast<Heightfield*>(_spanner.data());
|
||||
TempHeightfield* spanner = static_cast<TempHeightfield*>(_spanner.data());
|
||||
float increment = 1.0f / heightScale;
|
||||
if (!spanner) {
|
||||
_spannerBounds.minimum = glm::floor(_bounds.minimum / increment) * increment;
|
||||
|
@ -767,7 +768,7 @@ int HeightfieldClearFetchVisitor::visit(MetavoxelInfo& info) {
|
|||
Box innerBounds = _spannerBounds;
|
||||
innerBounds.maximum.x -= increment;
|
||||
innerBounds.maximum.z -= increment;
|
||||
_spanner = spanner = new Heightfield(innerBounds, increment, QByteArray(heightfieldArea, 0),
|
||||
_spanner = spanner = new TempHeightfield(innerBounds, increment, QByteArray(heightfieldArea, 0),
|
||||
QByteArray(heightfieldArea * DataBlock::COLOR_BYTES, 0), QByteArray(heightfieldArea, 0),
|
||||
QVector<SharedObjectPointer>());
|
||||
}
|
||||
|
|
783
libraries/metavoxels/src/Spanner.cpp
Normal file
783
libraries/metavoxels/src/Spanner.cpp
Normal file
|
@ -0,0 +1,783 @@
|
|||
//
|
||||
// Spanner.cpp
|
||||
// libraries/metavoxels/src
|
||||
//
|
||||
// Created by Andrzej Kapolka on 11/10/14.
|
||||
// Copyright 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
|
||||
//
|
||||
|
||||
#include <QThread>
|
||||
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include <GeometryUtil.h>
|
||||
|
||||
#include "Spanner.h"
|
||||
|
||||
REGISTER_META_OBJECT(Spanner)
|
||||
REGISTER_META_OBJECT(Sphere)
|
||||
REGISTER_META_OBJECT(Cuboid)
|
||||
REGISTER_META_OBJECT(StaticModel)
|
||||
REGISTER_META_OBJECT(Heightfield)
|
||||
|
||||
static int heightfieldHeightTypeId = registerSimpleMetaType<HeightfieldHeightPointer>();
|
||||
static int heightfieldColorTypeId = registerSimpleMetaType<HeightfieldColorPointer>();
|
||||
static int heightfieldMaterialTypeId = registerSimpleMetaType<HeightfieldMaterialPointer>();
|
||||
|
||||
const float DEFAULT_PLACEMENT_GRANULARITY = 0.01f;
|
||||
const float DEFAULT_VOXELIZATION_GRANULARITY = powf(2.0f, -3.0f);
|
||||
|
||||
Spanner::Spanner() :
|
||||
_renderer(NULL),
|
||||
_placementGranularity(DEFAULT_PLACEMENT_GRANULARITY),
|
||||
_voxelizationGranularity(DEFAULT_VOXELIZATION_GRANULARITY),
|
||||
_merged(false) {
|
||||
}
|
||||
|
||||
void Spanner::setBounds(const Box& bounds) {
|
||||
if (_bounds == bounds) {
|
||||
return;
|
||||
}
|
||||
emit boundsWillChange();
|
||||
emit boundsChanged(_bounds = bounds);
|
||||
}
|
||||
|
||||
bool Spanner::testAndSetVisited(int visit) {
|
||||
QMutexLocker locker(&_lastVisitsMutex);
|
||||
int& lastVisit = _lastVisits[QThread::currentThread()];
|
||||
if (lastVisit == visit) {
|
||||
return false;
|
||||
}
|
||||
lastVisit = visit;
|
||||
return true;
|
||||
}
|
||||
|
||||
SpannerRenderer* Spanner::getRenderer() {
|
||||
if (!_renderer) {
|
||||
QByteArray className = getRendererClassName();
|
||||
const QMetaObject* metaObject = Bitstream::getMetaObject(className);
|
||||
if (!metaObject) {
|
||||
qDebug() << "Unknown class name:" << className;
|
||||
metaObject = &SpannerRenderer::staticMetaObject;
|
||||
}
|
||||
_renderer = static_cast<SpannerRenderer*>(metaObject->newInstance());
|
||||
connect(this, &QObject::destroyed, _renderer, &QObject::deleteLater);
|
||||
_renderer->init(this);
|
||||
}
|
||||
return _renderer;
|
||||
}
|
||||
|
||||
bool Spanner::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
|
||||
return _bounds.findRayIntersection(origin, direction, distance);
|
||||
}
|
||||
|
||||
bool Spanner::hasOwnColors() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Spanner::hasOwnMaterials() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
QRgb Spanner::getColorAt(const glm::vec3& point) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Spanner::getMaterialAt(const glm::vec3& point) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
QVector<SharedObjectPointer>& Spanner::getMaterials() {
|
||||
static QVector<SharedObjectPointer> emptyMaterials;
|
||||
return emptyMaterials;
|
||||
}
|
||||
|
||||
bool Spanner::contains(const glm::vec3& point) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Spanner::intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray Spanner::getRendererClassName() const {
|
||||
return "SpannerRendererer";
|
||||
}
|
||||
|
||||
QAtomicInt Spanner::_nextVisit(1);
|
||||
|
||||
SpannerRenderer::SpannerRenderer() {
|
||||
}
|
||||
|
||||
void SpannerRenderer::init(Spanner* spanner) {
|
||||
_spanner = spanner;
|
||||
}
|
||||
|
||||
void SpannerRenderer::simulate(float deltaTime) {
|
||||
// nothing by default
|
||||
}
|
||||
|
||||
void SpannerRenderer::render(const glm::vec4& color, Mode mode) {
|
||||
// nothing by default
|
||||
}
|
||||
|
||||
bool SpannerRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
Transformable::Transformable() : _scale(1.0f) {
|
||||
}
|
||||
|
||||
void Transformable::setTranslation(const glm::vec3& translation) {
|
||||
if (_translation != translation) {
|
||||
emit translationChanged(_translation = translation);
|
||||
}
|
||||
}
|
||||
|
||||
void Transformable::setRotation(const glm::quat& rotation) {
|
||||
if (_rotation != rotation) {
|
||||
emit rotationChanged(_rotation = rotation);
|
||||
}
|
||||
}
|
||||
|
||||
void Transformable::setScale(float scale) {
|
||||
if (_scale != scale) {
|
||||
emit scaleChanged(_scale = scale);
|
||||
}
|
||||
}
|
||||
|
||||
ColorTransformable::ColorTransformable() :
|
||||
_color(Qt::white) {
|
||||
}
|
||||
|
||||
void ColorTransformable::setColor(const QColor& color) {
|
||||
if (_color != color) {
|
||||
emit colorChanged(_color = color);
|
||||
}
|
||||
}
|
||||
|
||||
Sphere::Sphere() {
|
||||
connect(this, SIGNAL(translationChanged(const glm::vec3&)), SLOT(updateBounds()));
|
||||
connect(this, SIGNAL(scaleChanged(float)), SLOT(updateBounds()));
|
||||
updateBounds();
|
||||
}
|
||||
|
||||
bool Sphere::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
|
||||
return findRaySphereIntersection(origin, direction, getTranslation(), getScale(), distance);
|
||||
}
|
||||
|
||||
bool Sphere::contains(const glm::vec3& point) {
|
||||
return glm::distance(point, getTranslation()) <= getScale();
|
||||
}
|
||||
|
||||
bool Sphere::intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal) {
|
||||
glm::vec3 relativeStart = start - getTranslation();
|
||||
glm::vec3 vector = end - start;
|
||||
float a = glm::dot(vector, vector);
|
||||
if (a == 0.0f) {
|
||||
return false;
|
||||
}
|
||||
float b = glm::dot(relativeStart, vector);
|
||||
float radicand = b * b - a * (glm::dot(relativeStart, relativeStart) - getScale() * getScale());
|
||||
if (radicand < 0.0f) {
|
||||
return false;
|
||||
}
|
||||
float radical = glm::sqrt(radicand);
|
||||
float first = (-b - radical) / a;
|
||||
if (first >= 0.0f && first <= 1.0f) {
|
||||
distance = first;
|
||||
normal = glm::normalize(relativeStart + vector * distance);
|
||||
return true;
|
||||
}
|
||||
float second = (-b + radical) / a;
|
||||
if (second >= 0.0f && second <= 1.0f) {
|
||||
distance = second;
|
||||
normal = glm::normalize(relativeStart + vector * distance);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray Sphere::getRendererClassName() const {
|
||||
return "SphereRenderer";
|
||||
}
|
||||
|
||||
void Sphere::updateBounds() {
|
||||
glm::vec3 extent(getScale(), getScale(), getScale());
|
||||
setBounds(Box(getTranslation() - extent, getTranslation() + extent));
|
||||
}
|
||||
|
||||
Cuboid::Cuboid() :
|
||||
_aspectY(1.0f),
|
||||
_aspectZ(1.0f) {
|
||||
|
||||
connect(this, &Cuboid::translationChanged, this, &Cuboid::updateBoundsAndPlanes);
|
||||
connect(this, &Cuboid::rotationChanged, this, &Cuboid::updateBoundsAndPlanes);
|
||||
connect(this, &Cuboid::scaleChanged, this, &Cuboid::updateBoundsAndPlanes);
|
||||
connect(this, &Cuboid::aspectYChanged, this, &Cuboid::updateBoundsAndPlanes);
|
||||
connect(this, &Cuboid::aspectZChanged, this, &Cuboid::updateBoundsAndPlanes);
|
||||
updateBoundsAndPlanes();
|
||||
}
|
||||
|
||||
void Cuboid::setAspectY(float aspectY) {
|
||||
if (_aspectY != aspectY) {
|
||||
emit aspectYChanged(_aspectY = aspectY);
|
||||
}
|
||||
}
|
||||
|
||||
void Cuboid::setAspectZ(float aspectZ) {
|
||||
if (_aspectZ != aspectZ) {
|
||||
emit aspectZChanged(_aspectZ = aspectZ);
|
||||
}
|
||||
}
|
||||
|
||||
bool Cuboid::contains(const glm::vec3& point) {
|
||||
glm::vec4 point4(point, 1.0f);
|
||||
for (int i = 0; i < PLANE_COUNT; i++) {
|
||||
if (glm::dot(_planes[i], point4) > 0.0f) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Cuboid::intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal) {
|
||||
glm::vec4 start4(start, 1.0f);
|
||||
glm::vec4 vector = glm::vec4(end - start, 0.0f);
|
||||
for (int i = 0; i < PLANE_COUNT; i++) {
|
||||
// first check the segment against the plane
|
||||
float divisor = glm::dot(_planes[i], vector);
|
||||
if (glm::abs(divisor) < EPSILON) {
|
||||
continue;
|
||||
}
|
||||
float t = -glm::dot(_planes[i], start4) / divisor;
|
||||
if (t < 0.0f || t > 1.0f) {
|
||||
continue;
|
||||
}
|
||||
// now that we've established that it intersects the plane, check against the other sides
|
||||
glm::vec4 point = start4 + vector * t;
|
||||
const int PLANES_PER_AXIS = 2;
|
||||
int indexOffset = ((i / PLANES_PER_AXIS) + 1) * PLANES_PER_AXIS;
|
||||
for (int j = 0; j < PLANE_COUNT - PLANES_PER_AXIS; j++) {
|
||||
if (glm::dot(_planes[(indexOffset + j) % PLANE_COUNT], point) > 0.0f) {
|
||||
goto outerContinue;
|
||||
}
|
||||
}
|
||||
distance = t;
|
||||
normal = glm::vec3(_planes[i]);
|
||||
return true;
|
||||
|
||||
outerContinue: ;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray Cuboid::getRendererClassName() const {
|
||||
return "CuboidRenderer";
|
||||
}
|
||||
|
||||
void Cuboid::updateBoundsAndPlanes() {
|
||||
glm::vec3 extent(getScale(), getScale() * _aspectY, getScale() * _aspectZ);
|
||||
glm::mat4 rotationMatrix = glm::mat4_cast(getRotation());
|
||||
setBounds(glm::translate(getTranslation()) * rotationMatrix * Box(-extent, extent));
|
||||
|
||||
glm::vec4 translation4 = glm::vec4(getTranslation(), 1.0f);
|
||||
_planes[0] = glm::vec4(glm::vec3(rotationMatrix[0]), -glm::dot(rotationMatrix[0], translation4) - getScale());
|
||||
_planes[1] = glm::vec4(glm::vec3(-rotationMatrix[0]), glm::dot(rotationMatrix[0], translation4) - getScale());
|
||||
_planes[2] = glm::vec4(glm::vec3(rotationMatrix[1]), -glm::dot(rotationMatrix[1], translation4) - getScale() * _aspectY);
|
||||
_planes[3] = glm::vec4(glm::vec3(-rotationMatrix[1]), glm::dot(rotationMatrix[1], translation4) - getScale() * _aspectY);
|
||||
_planes[4] = glm::vec4(glm::vec3(rotationMatrix[2]), -glm::dot(rotationMatrix[2], translation4) - getScale() * _aspectZ);
|
||||
_planes[5] = glm::vec4(glm::vec3(-rotationMatrix[2]), glm::dot(rotationMatrix[2], translation4) - getScale() * _aspectZ);
|
||||
}
|
||||
|
||||
StaticModel::StaticModel() {
|
||||
}
|
||||
|
||||
void StaticModel::setURL(const QUrl& url) {
|
||||
if (_url != url) {
|
||||
emit urlChanged(_url = url);
|
||||
}
|
||||
}
|
||||
|
||||
bool StaticModel::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
|
||||
// delegate to renderer, if we have one
|
||||
return _renderer ? _renderer->findRayIntersection(origin, direction, distance) :
|
||||
Spanner::findRayIntersection(origin, direction, distance);
|
||||
}
|
||||
|
||||
QByteArray StaticModel::getRendererClassName() const {
|
||||
return "StaticModelRenderer";
|
||||
}
|
||||
|
||||
const float EIGHT_BIT_MAXIMUM = 255.0f;
|
||||
|
||||
TempHeightfield::TempHeightfield(const Box& bounds, float increment, const QByteArray& height, const QByteArray& color,
|
||||
const QByteArray& material, const QVector<SharedObjectPointer>& materials) :
|
||||
_increment(increment),
|
||||
_width((int)glm::round((bounds.maximum.x - bounds.minimum.x) / increment) + 1),
|
||||
_heightScale((bounds.maximum.y - bounds.minimum.y) / EIGHT_BIT_MAXIMUM),
|
||||
_height(height),
|
||||
_color(color),
|
||||
_material(material),
|
||||
_materials(materials) {
|
||||
|
||||
setBounds(bounds);
|
||||
}
|
||||
|
||||
bool TempHeightfield::hasOwnColors() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TempHeightfield::hasOwnMaterials() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
QRgb TempHeightfield::getColorAt(const glm::vec3& point) {
|
||||
glm::vec3 relative = (point - getBounds().minimum) / _increment;
|
||||
glm::vec3 floors = glm::floor(relative);
|
||||
glm::vec3 ceils = glm::ceil(relative);
|
||||
glm::vec3 fracts = glm::fract(relative);
|
||||
int floorX = (int)floors.x;
|
||||
int floorZ = (int)floors.z;
|
||||
int ceilX = (int)ceils.x;
|
||||
int ceilZ = (int)ceils.z;
|
||||
const uchar* src = (const uchar*)_color.constData();
|
||||
const uchar* upperLeft = src + (floorZ * _width + floorX) * DataBlock::COLOR_BYTES;
|
||||
const uchar* lowerRight = src + (ceilZ * _width + ceilX) * DataBlock::COLOR_BYTES;
|
||||
glm::vec3 interpolatedColor = glm::mix(glm::vec3(upperLeft[0], upperLeft[1], upperLeft[2]),
|
||||
glm::vec3(lowerRight[0], lowerRight[1], lowerRight[2]), fracts.z);
|
||||
|
||||
// the final vertex (and thus which triangle we check) depends on which half we're on
|
||||
if (fracts.x >= fracts.z) {
|
||||
const uchar* upperRight = src + (floorZ * _width + ceilX) * DataBlock::COLOR_BYTES;
|
||||
interpolatedColor = glm::mix(interpolatedColor, glm::mix(glm::vec3(upperRight[0], upperRight[1], upperRight[2]),
|
||||
glm::vec3(lowerRight[0], lowerRight[1], lowerRight[2]), fracts.z), (fracts.x - fracts.z) / (1.0f - fracts.z));
|
||||
|
||||
} else {
|
||||
const uchar* lowerLeft = src + (ceilZ * _width + floorX) * DataBlock::COLOR_BYTES;
|
||||
interpolatedColor = glm::mix(glm::mix(glm::vec3(upperLeft[0], upperLeft[1], upperLeft[2]),
|
||||
glm::vec3(lowerLeft[0], lowerLeft[1], lowerLeft[2]), fracts.z), interpolatedColor, fracts.x / fracts.z);
|
||||
}
|
||||
return qRgb(interpolatedColor.r, interpolatedColor.g, interpolatedColor.b);
|
||||
}
|
||||
|
||||
int TempHeightfield::getMaterialAt(const glm::vec3& point) {
|
||||
glm::vec3 relative = (point - getBounds().minimum) / _increment;
|
||||
const uchar* src = (const uchar*)_material.constData();
|
||||
return src[(int)glm::round(relative.z) * _width + (int)glm::round(relative.x)];
|
||||
}
|
||||
|
||||
QVector<SharedObjectPointer>& TempHeightfield::getMaterials() {
|
||||
return _materials;
|
||||
}
|
||||
|
||||
bool TempHeightfield::contains(const glm::vec3& point) {
|
||||
if (!getBounds().contains(point)) {
|
||||
return false;
|
||||
}
|
||||
glm::vec3 relative = (point - getBounds().minimum) / _increment;
|
||||
glm::vec3 floors = glm::floor(relative);
|
||||
glm::vec3 ceils = glm::ceil(relative);
|
||||
glm::vec3 fracts = glm::fract(relative);
|
||||
int floorX = (int)floors.x;
|
||||
int floorZ = (int)floors.z;
|
||||
int ceilX = (int)ceils.x;
|
||||
int ceilZ = (int)ceils.z;
|
||||
const uchar* src = (const uchar*)_height.constData();
|
||||
float upperLeft = src[floorZ * _width + floorX];
|
||||
float lowerRight = src[ceilZ * _width + ceilX];
|
||||
float interpolatedHeight = glm::mix(upperLeft, lowerRight, fracts.z);
|
||||
|
||||
// the final vertex (and thus which triangle we check) depends on which half we're on
|
||||
if (fracts.x >= fracts.z) {
|
||||
float upperRight = src[floorZ * _width + ceilX];
|
||||
interpolatedHeight = glm::mix(interpolatedHeight, glm::mix(upperRight, lowerRight, fracts.z),
|
||||
(fracts.x - fracts.z) / (1.0f - fracts.z));
|
||||
|
||||
} else {
|
||||
float lowerLeft = src[ceilZ * _width + floorX];
|
||||
interpolatedHeight = glm::mix(glm::mix(upperLeft, lowerLeft, fracts.z), interpolatedHeight, fracts.x / fracts.z);
|
||||
}
|
||||
return interpolatedHeight != 0.0f && point.y <= interpolatedHeight * _heightScale + getBounds().minimum.y;
|
||||
}
|
||||
|
||||
bool TempHeightfield::intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal) {
|
||||
// find the initial location in heightfield coordinates
|
||||
float rayDistance;
|
||||
glm::vec3 direction = end - start;
|
||||
if (!getBounds().findRayIntersection(start, direction, rayDistance) || rayDistance > 1.0f) {
|
||||
return false;
|
||||
}
|
||||
glm::vec3 entry = start + direction * rayDistance;
|
||||
const float DISTANCE_THRESHOLD = 0.001f;
|
||||
if (glm::abs(entry.x - getBounds().minimum.x) < DISTANCE_THRESHOLD) {
|
||||
normal = glm::vec3(-1.0f, 0.0f, 0.0f);
|
||||
distance = rayDistance;
|
||||
return true;
|
||||
|
||||
} else if (glm::abs(entry.x - getBounds().maximum.x) < DISTANCE_THRESHOLD) {
|
||||
normal = glm::vec3(1.0f, 0.0f, 0.0f);
|
||||
distance = rayDistance;
|
||||
return true;
|
||||
|
||||
} else if (glm::abs(entry.y - getBounds().minimum.y) < DISTANCE_THRESHOLD) {
|
||||
normal = glm::vec3(0.0f, -1.0f, 0.0f);
|
||||
distance = rayDistance;
|
||||
return true;
|
||||
|
||||
} else if (glm::abs(entry.y - getBounds().maximum.y) < DISTANCE_THRESHOLD) {
|
||||
normal = glm::vec3(0.0f, 1.0f, 0.0f);
|
||||
distance = rayDistance;
|
||||
return true;
|
||||
|
||||
} else if (glm::abs(entry.z - getBounds().minimum.z) < DISTANCE_THRESHOLD) {
|
||||
normal = glm::vec3(0.0f, 0.0f, -1.0f);
|
||||
distance = rayDistance;
|
||||
return true;
|
||||
|
||||
} else if (glm::abs(entry.z - getBounds().maximum.z) < DISTANCE_THRESHOLD) {
|
||||
normal = glm::vec3(0.0f, 0.0f, 1.0f);
|
||||
distance = rayDistance;
|
||||
return true;
|
||||
}
|
||||
entry = (entry - getBounds().minimum) / _increment;
|
||||
glm::vec3 floors = glm::floor(entry);
|
||||
glm::vec3 ceils = glm::ceil(entry);
|
||||
if (floors.x == ceils.x) {
|
||||
if (direction.x > 0.0f) {
|
||||
ceils.x += 1.0f;
|
||||
} else {
|
||||
floors.x -= 1.0f;
|
||||
}
|
||||
}
|
||||
if (floors.z == ceils.z) {
|
||||
if (direction.z > 0.0f) {
|
||||
ceils.z += 1.0f;
|
||||
} else {
|
||||
floors.z -= 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
bool withinBounds = true;
|
||||
float accumulatedDistance = 0.0f;
|
||||
const uchar* src = (const uchar*)_height.constData();
|
||||
int highestX = _width - 1;
|
||||
float highestY = (getBounds().maximum.y - getBounds().minimum.y) / _increment;
|
||||
int highestZ = (int)glm::round((getBounds().maximum.z - getBounds().minimum.z) / _increment);
|
||||
float heightScale = _heightScale / _increment;
|
||||
while (withinBounds && accumulatedDistance <= 1.0f) {
|
||||
// find the heights at the corners of the current cell
|
||||
int floorX = qMin(qMax((int)floors.x, 0), highestX);
|
||||
int floorZ = qMin(qMax((int)floors.z, 0), highestZ);
|
||||
int ceilX = qMin(qMax((int)ceils.x, 0), highestX);
|
||||
int ceilZ = qMin(qMax((int)ceils.z, 0), highestZ);
|
||||
float upperLeft = src[floorZ * _width + floorX] * heightScale;
|
||||
float upperRight = src[floorZ * _width + ceilX] * heightScale;
|
||||
float lowerLeft = src[ceilZ * _width + floorX] * heightScale;
|
||||
float lowerRight = src[ceilZ * _width + ceilX] * heightScale;
|
||||
|
||||
// find the distance to the next x coordinate
|
||||
float xDistance = FLT_MAX;
|
||||
if (direction.x > 0.0f) {
|
||||
xDistance = (ceils.x - entry.x) / direction.x;
|
||||
} else if (direction.x < 0.0f) {
|
||||
xDistance = (floors.x - entry.x) / direction.x;
|
||||
}
|
||||
|
||||
// and the distance to the next z coordinate
|
||||
float zDistance = FLT_MAX;
|
||||
if (direction.z > 0.0f) {
|
||||
zDistance = (ceils.z - entry.z) / direction.z;
|
||||
} else if (direction.z < 0.0f) {
|
||||
zDistance = (floors.z - entry.z) / direction.z;
|
||||
}
|
||||
|
||||
// the exit distance is the lower of those two
|
||||
float exitDistance = qMin(xDistance, zDistance);
|
||||
glm::vec3 exit, nextFloors = floors, nextCeils = ceils;
|
||||
if (exitDistance == FLT_MAX) {
|
||||
withinBounds = false; // line points upwards/downwards; check this cell only
|
||||
|
||||
} else {
|
||||
// find the exit point and the next cell, and determine whether it's still within the bounds
|
||||
exit = entry + exitDistance * direction;
|
||||
withinBounds = (exit.y >= 0.0f && exit.y <= highestY);
|
||||
if (exitDistance == xDistance) {
|
||||
if (direction.x > 0.0f) {
|
||||
nextFloors.x += 1.0f;
|
||||
withinBounds &= (nextCeils.x += 1.0f) <= highestX;
|
||||
} else {
|
||||
withinBounds &= (nextFloors.x -= 1.0f) >= 0.0f;
|
||||
nextCeils.x -= 1.0f;
|
||||
}
|
||||
}
|
||||
if (exitDistance == zDistance) {
|
||||
if (direction.z > 0.0f) {
|
||||
nextFloors.z += 1.0f;
|
||||
withinBounds &= (nextCeils.z += 1.0f) <= highestZ;
|
||||
} else {
|
||||
withinBounds &= (nextFloors.z -= 1.0f) >= 0.0f;
|
||||
nextCeils.z -= 1.0f;
|
||||
}
|
||||
}
|
||||
// check the vertical range of the ray against the ranges of the cell heights
|
||||
if (qMin(entry.y, exit.y) > qMax(qMax(upperLeft, upperRight), qMax(lowerLeft, lowerRight)) ||
|
||||
qMax(entry.y, exit.y) < qMin(qMin(upperLeft, upperRight), qMin(lowerLeft, lowerRight))) {
|
||||
entry = exit;
|
||||
floors = nextFloors;
|
||||
ceils = nextCeils;
|
||||
accumulatedDistance += exitDistance;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// having passed the bounds check, we must check against the planes
|
||||
glm::vec3 relativeEntry = entry - glm::vec3(floors.x, upperLeft, floors.z);
|
||||
|
||||
// first check the triangle including the Z+ segment
|
||||
glm::vec3 lowerNormal(lowerLeft - lowerRight, 1.0f, upperLeft - lowerLeft);
|
||||
float lowerProduct = glm::dot(lowerNormal, direction);
|
||||
if (lowerProduct != 0.0f) {
|
||||
float planeDistance = -glm::dot(lowerNormal, relativeEntry) / lowerProduct;
|
||||
glm::vec3 intersection = relativeEntry + planeDistance * direction;
|
||||
if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f &&
|
||||
intersection.z >= intersection.x) {
|
||||
distance = rayDistance + (accumulatedDistance + planeDistance) * _increment;
|
||||
normal = glm::normalize(lowerNormal);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// then the one with the X+ segment
|
||||
glm::vec3 upperNormal(upperLeft - upperRight, 1.0f, upperRight - lowerRight);
|
||||
float upperProduct = glm::dot(upperNormal, direction);
|
||||
if (upperProduct != 0.0f) {
|
||||
float planeDistance = -glm::dot(upperNormal, relativeEntry) / upperProduct;
|
||||
glm::vec3 intersection = relativeEntry + planeDistance * direction;
|
||||
if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f &&
|
||||
intersection.x >= intersection.z) {
|
||||
distance = rayDistance + (accumulatedDistance + planeDistance) * _increment;
|
||||
normal = glm::normalize(upperNormal);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// no joy; continue on our way
|
||||
entry = exit;
|
||||
floors = nextFloors;
|
||||
ceils = nextCeils;
|
||||
accumulatedDistance += exitDistance;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
HeightfieldData::HeightfieldData(int width) :
|
||||
_width(width) {
|
||||
}
|
||||
|
||||
HeightfieldHeight::HeightfieldHeight(int width, const QVector<quint16>& contents) :
|
||||
HeightfieldData(width),
|
||||
_contents(contents) {
|
||||
}
|
||||
|
||||
HeightfieldHeight::HeightfieldHeight(Bitstream& in, int bytes) {
|
||||
}
|
||||
|
||||
HeightfieldHeight::HeightfieldHeight(Bitstream& in, int bytes, const HeightfieldHeightPointer& reference) {
|
||||
}
|
||||
|
||||
void HeightfieldHeight::write(Bitstream& out) {
|
||||
QMutexLocker locker(&_encodedMutex);
|
||||
if (_encoded.isEmpty()) {
|
||||
|
||||
}
|
||||
out << _encoded.size();
|
||||
out.writeAligned(_encoded);
|
||||
}
|
||||
|
||||
void HeightfieldHeight::writeDelta(Bitstream& out, const HeightfieldHeightPointer& reference) {
|
||||
}
|
||||
|
||||
Bitstream& operator<<(Bitstream& out, const HeightfieldHeightPointer& value) {
|
||||
if (value) {
|
||||
value->write(out);
|
||||
} else {
|
||||
out << 0;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Bitstream& operator>>(Bitstream& in, HeightfieldHeightPointer& value) {
|
||||
int size;
|
||||
in >> size;
|
||||
if (size == 0) {
|
||||
value = HeightfieldHeightPointer();
|
||||
} else {
|
||||
value = new HeightfieldHeight(0, QVector<quint16>());
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
template<> void Bitstream::writeRawDelta(const HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference) {
|
||||
}
|
||||
|
||||
template<> void Bitstream::readRawDelta(HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference) {
|
||||
}
|
||||
|
||||
HeightfieldColor::HeightfieldColor(int width, const QByteArray& contents) :
|
||||
HeightfieldData(width),
|
||||
_contents(contents) {
|
||||
}
|
||||
|
||||
HeightfieldColor::HeightfieldColor(Bitstream& in, int bytes) {
|
||||
}
|
||||
|
||||
HeightfieldColor::HeightfieldColor(Bitstream& in, int bytes, const HeightfieldColorPointer& reference) {
|
||||
}
|
||||
|
||||
void HeightfieldColor::write(Bitstream& out) {
|
||||
QMutexLocker locker(&_encodedMutex);
|
||||
if (_encoded.isEmpty()) {
|
||||
|
||||
}
|
||||
out << _encoded.size();
|
||||
out.writeAligned(_encoded);
|
||||
}
|
||||
|
||||
void HeightfieldColor::writeDelta(Bitstream& out, const HeightfieldColorPointer& reference) {
|
||||
}
|
||||
|
||||
Bitstream& operator<<(Bitstream& out, const HeightfieldColorPointer& value) {
|
||||
if (value) {
|
||||
value->write(out);
|
||||
} else {
|
||||
out << 0;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Bitstream& operator>>(Bitstream& in, HeightfieldColorPointer& value) {
|
||||
int size;
|
||||
in >> size;
|
||||
if (size == 0) {
|
||||
value = HeightfieldColorPointer();
|
||||
} else {
|
||||
value = new HeightfieldColor(0, QByteArray());
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
template<> void Bitstream::writeRawDelta(const HeightfieldColorPointer& value, const HeightfieldColorPointer& reference) {
|
||||
|
||||
}
|
||||
|
||||
template<> void Bitstream::readRawDelta(HeightfieldColorPointer& value, const HeightfieldColorPointer& reference) {
|
||||
}
|
||||
|
||||
HeightfieldMaterial::HeightfieldMaterial(int width, const QByteArray& contents,
|
||||
const QVector<SharedObjectPointer>& materials) :
|
||||
HeightfieldData(width),
|
||||
_contents(contents),
|
||||
_materials(materials) {
|
||||
}
|
||||
|
||||
HeightfieldMaterial::HeightfieldMaterial(Bitstream& in, int bytes) {
|
||||
}
|
||||
|
||||
HeightfieldMaterial::HeightfieldMaterial(Bitstream& in, int bytes, const HeightfieldMaterialPointer& reference) {
|
||||
}
|
||||
|
||||
void HeightfieldMaterial::write(Bitstream& out) {
|
||||
QMutexLocker locker(&_encodedMutex);
|
||||
if (_encoded.isEmpty()) {
|
||||
|
||||
}
|
||||
out << _encoded.size();
|
||||
out.writeAligned(_encoded);
|
||||
out << _materials;
|
||||
}
|
||||
|
||||
void HeightfieldMaterial::writeDelta(Bitstream& out, const HeightfieldMaterialPointer& reference) {
|
||||
}
|
||||
|
||||
Bitstream& operator<<(Bitstream& out, const HeightfieldMaterialPointer& value) {
|
||||
if (value) {
|
||||
value->write(out);
|
||||
} else {
|
||||
out << 0;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Bitstream& operator>>(Bitstream& in, HeightfieldMaterialPointer& value) {
|
||||
int size;
|
||||
in >> size;
|
||||
if (size == 0) {
|
||||
value = HeightfieldMaterialPointer();
|
||||
} else {
|
||||
value = new HeightfieldMaterial(0, QByteArray(), QVector<SharedObjectPointer>());
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
template<> void Bitstream::writeRawDelta(const HeightfieldMaterialPointer& value,
|
||||
const HeightfieldMaterialPointer& reference) {
|
||||
}
|
||||
|
||||
template<> void Bitstream::readRawDelta(HeightfieldMaterialPointer& value, const HeightfieldMaterialPointer& reference) {
|
||||
}
|
||||
|
||||
Heightfield::Heightfield() :
|
||||
_aspectY(1.0f),
|
||||
_aspectZ(1.0f) {
|
||||
|
||||
connect(this, &Heightfield::translationChanged, this, &Heightfield::updateBounds);
|
||||
connect(this, &Heightfield::rotationChanged, this, &Heightfield::updateBounds);
|
||||
connect(this, &Heightfield::scaleChanged, this, &Heightfield::updateBounds);
|
||||
connect(this, &Heightfield::aspectYChanged, this, &Heightfield::updateBounds);
|
||||
connect(this, &Heightfield::aspectZChanged, this, &Heightfield::updateBounds);
|
||||
updateBounds();
|
||||
}
|
||||
|
||||
void Heightfield::setAspectY(float aspectY) {
|
||||
if (_aspectY != aspectY) {
|
||||
emit aspectYChanged(_aspectY = aspectY);
|
||||
}
|
||||
}
|
||||
|
||||
void Heightfield::setAspectZ(float aspectZ) {
|
||||
if (_aspectZ != aspectZ) {
|
||||
emit aspectZChanged(_aspectZ = aspectZ);
|
||||
}
|
||||
}
|
||||
|
||||
void Heightfield::setHeight(const HeightfieldHeightPointer& height) {
|
||||
if (_height != height) {
|
||||
emit heightChanged(_height = height);
|
||||
}
|
||||
}
|
||||
|
||||
void Heightfield::setColor(const HeightfieldColorPointer& color) {
|
||||
if (_color != color) {
|
||||
emit colorChanged(_color = color);
|
||||
}
|
||||
}
|
||||
|
||||
void Heightfield::setMaterial(const HeightfieldMaterialPointer& material) {
|
||||
if (_material != material) {
|
||||
emit materialChanged(_material = material);
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray Heightfield::getRendererClassName() const {
|
||||
return "HeightfieldRenderer";
|
||||
}
|
||||
|
||||
void Heightfield::updateBounds() {
|
||||
glm::vec3 extent(getScale(), getScale() * _aspectY, getScale() * _aspectZ);
|
||||
glm::mat4 rotationMatrix = glm::mat4_cast(getRotation());
|
||||
setBounds(glm::translate(getTranslation()) * rotationMatrix * Box(-extent, extent));
|
||||
}
|
454
libraries/metavoxels/src/Spanner.h
Normal file
454
libraries/metavoxels/src/Spanner.h
Normal file
|
@ -0,0 +1,454 @@
|
|||
//
|
||||
// Spanner.h
|
||||
// libraries/metavoxels/src
|
||||
//
|
||||
// Created by Andrzej Kapolka on 11/10/14.
|
||||
// Copyright 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_Spanner_h
|
||||
#define hifi_Spanner_h
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "AttributeRegistry.h"
|
||||
#include "MetavoxelUtil.h"
|
||||
|
||||
class HeightfieldColor;
|
||||
class HeightfieldHeight;
|
||||
class HeightfieldMaterial;
|
||||
class SpannerRenderer;
|
||||
|
||||
/// An object that spans multiple octree cells.
|
||||
class Spanner : public SharedObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(Box bounds MEMBER _bounds WRITE setBounds NOTIFY boundsChanged DESIGNABLE false)
|
||||
Q_PROPERTY(float placementGranularity MEMBER _placementGranularity DESIGNABLE false)
|
||||
Q_PROPERTY(float voxelizationGranularity MEMBER _voxelizationGranularity DESIGNABLE false)
|
||||
|
||||
public:
|
||||
|
||||
/// Returns the value of the global visit counter and increments it.
|
||||
static int getAndIncrementNextVisit() { return _nextVisit.fetchAndAddOrdered(1); }
|
||||
|
||||
Spanner();
|
||||
|
||||
void setBounds(const Box& bounds);
|
||||
const Box& getBounds() const { return _bounds; }
|
||||
|
||||
void setPlacementGranularity(float granularity) { _placementGranularity = granularity; }
|
||||
float getPlacementGranularity() const { return _placementGranularity; }
|
||||
|
||||
void setVoxelizationGranularity(float granularity) { _voxelizationGranularity = granularity; }
|
||||
float getVoxelizationGranularity() const { return _voxelizationGranularity; }
|
||||
|
||||
void setMerged(bool merged) { _merged = merged; }
|
||||
bool isMerged() const { return _merged; }
|
||||
|
||||
/// Checks whether we've visited this object on the current traversal. If we have, returns false.
|
||||
/// If we haven't, sets the last visit identifier and returns true.
|
||||
bool testAndSetVisited(int visit);
|
||||
|
||||
/// Returns a pointer to the renderer, creating it if necessary.
|
||||
SpannerRenderer* getRenderer();
|
||||
|
||||
/// Finds the intersection between the described ray and this spanner.
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
|
||||
|
||||
/// Checks whether this spanner has its own colors.
|
||||
virtual bool hasOwnColors() const;
|
||||
|
||||
/// Checks whether this spanner has its own materials.
|
||||
virtual bool hasOwnMaterials() const;
|
||||
|
||||
/// Checks whether the spanner contains the specified point.
|
||||
virtual bool contains(const glm::vec3& point);
|
||||
|
||||
/// Retrieves the color at the specified point.
|
||||
virtual QRgb getColorAt(const glm::vec3& point);
|
||||
|
||||
/// Retrieves the material at the specified point.
|
||||
virtual int getMaterialAt(const glm::vec3& point);
|
||||
|
||||
/// Retrieves a reference to the list of materials.
|
||||
virtual QVector<SharedObjectPointer>& getMaterials();
|
||||
|
||||
/// Finds the intersection, if any, between the specified line segment and the spanner.
|
||||
virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal);
|
||||
|
||||
signals:
|
||||
|
||||
void boundsWillChange();
|
||||
void boundsChanged(const Box& bounds);
|
||||
|
||||
protected:
|
||||
|
||||
SpannerRenderer* _renderer;
|
||||
|
||||
/// Returns the name of the class to instantiate in order to render this spanner.
|
||||
virtual QByteArray getRendererClassName() const;
|
||||
|
||||
private:
|
||||
|
||||
Box _bounds;
|
||||
float _placementGranularity;
|
||||
float _voxelizationGranularity;
|
||||
bool _merged;
|
||||
QHash<QThread*, int> _lastVisits; ///< last visit identifiers for each thread
|
||||
QMutex _lastVisitsMutex;
|
||||
|
||||
static QAtomicInt _nextVisit; ///< the global visit counter
|
||||
};
|
||||
|
||||
/// Base class for objects that can render spanners.
|
||||
class SpannerRenderer : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
enum Mode { DEFAULT_MODE, DIFFUSE_MODE, NORMAL_MODE };
|
||||
|
||||
Q_INVOKABLE SpannerRenderer();
|
||||
|
||||
virtual void init(Spanner* spanner);
|
||||
virtual void simulate(float deltaTime);
|
||||
virtual void render(const glm::vec4& color, Mode mode);
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
|
||||
|
||||
protected:
|
||||
|
||||
Spanner* _spanner;
|
||||
};
|
||||
|
||||
/// An object with a 3D transform.
|
||||
class Transformable : public Spanner {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(glm::vec3 translation MEMBER _translation WRITE setTranslation NOTIFY translationChanged)
|
||||
Q_PROPERTY(glm::quat rotation MEMBER _rotation WRITE setRotation NOTIFY rotationChanged)
|
||||
Q_PROPERTY(float scale MEMBER _scale WRITE setScale NOTIFY scaleChanged)
|
||||
|
||||
public:
|
||||
|
||||
Transformable();
|
||||
|
||||
void setTranslation(const glm::vec3& translation);
|
||||
const glm::vec3& getTranslation() const { return _translation; }
|
||||
|
||||
void setRotation(const glm::quat& rotation);
|
||||
const glm::quat& getRotation() const { return _rotation; }
|
||||
|
||||
void setScale(float scale);
|
||||
float getScale() const { return _scale; }
|
||||
|
||||
signals:
|
||||
|
||||
void translationChanged(const glm::vec3& translation);
|
||||
void rotationChanged(const glm::quat& rotation);
|
||||
void scaleChanged(float scale);
|
||||
|
||||
private:
|
||||
|
||||
glm::vec3 _translation;
|
||||
glm::quat _rotation;
|
||||
float _scale;
|
||||
};
|
||||
|
||||
/// A transformable object with a color.
|
||||
class ColorTransformable : public Transformable {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QColor color MEMBER _color WRITE setColor NOTIFY colorChanged DESIGNABLE false)
|
||||
|
||||
public:
|
||||
|
||||
ColorTransformable();
|
||||
|
||||
void setColor(const QColor& color);
|
||||
const QColor& getColor() const { return _color; }
|
||||
|
||||
signals:
|
||||
|
||||
void colorChanged(const QColor& color);
|
||||
|
||||
protected:
|
||||
|
||||
QColor _color;
|
||||
};
|
||||
|
||||
/// A sphere.
|
||||
class Sphere : public ColorTransformable {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
Q_INVOKABLE Sphere();
|
||||
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
|
||||
virtual bool contains(const glm::vec3& point);
|
||||
virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal);
|
||||
|
||||
protected:
|
||||
|
||||
virtual QByteArray getRendererClassName() const;
|
||||
|
||||
private slots:
|
||||
|
||||
void updateBounds();
|
||||
};
|
||||
|
||||
/// A cuboid.
|
||||
class Cuboid : public ColorTransformable {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(float aspectY MEMBER _aspectY WRITE setAspectY NOTIFY aspectYChanged)
|
||||
Q_PROPERTY(float aspectZ MEMBER _aspectZ WRITE setAspectZ NOTIFY aspectZChanged)
|
||||
|
||||
public:
|
||||
|
||||
Q_INVOKABLE Cuboid();
|
||||
|
||||
void setAspectY(float aspectY);
|
||||
float getAspectY() const { return _aspectY; }
|
||||
|
||||
void setAspectZ(float aspectZ);
|
||||
float getAspectZ() const { return _aspectZ; }
|
||||
|
||||
virtual bool contains(const glm::vec3& point);
|
||||
virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal);
|
||||
|
||||
signals:
|
||||
|
||||
void aspectYChanged(float aspectY);
|
||||
void aspectZChanged(float aspectZ);
|
||||
|
||||
protected:
|
||||
|
||||
virtual QByteArray getRendererClassName() const;
|
||||
|
||||
private slots:
|
||||
|
||||
void updateBoundsAndPlanes();
|
||||
|
||||
private:
|
||||
|
||||
float _aspectY;
|
||||
float _aspectZ;
|
||||
|
||||
static const int PLANE_COUNT = 6;
|
||||
glm::vec4 _planes[PLANE_COUNT];
|
||||
};
|
||||
|
||||
/// A static 3D model loaded from the network.
|
||||
class StaticModel : public Transformable {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QUrl url MEMBER _url WRITE setURL NOTIFY urlChanged)
|
||||
|
||||
public:
|
||||
|
||||
Q_INVOKABLE StaticModel();
|
||||
|
||||
void setURL(const QUrl& url);
|
||||
const QUrl& getURL() const { return _url; }
|
||||
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
|
||||
|
||||
signals:
|
||||
|
||||
void urlChanged(const QUrl& url);
|
||||
|
||||
protected:
|
||||
|
||||
virtual QByteArray getRendererClassName() const;
|
||||
|
||||
private:
|
||||
|
||||
QUrl _url;
|
||||
};
|
||||
|
||||
/// A heightfield represented as a spanner.
|
||||
class TempHeightfield : public Transformable {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
TempHeightfield(const Box& bounds, float increment, const QByteArray& height, const QByteArray& color,
|
||||
const QByteArray& material, const QVector<SharedObjectPointer>& materials);
|
||||
|
||||
QByteArray& getHeight() { return _height; }
|
||||
QByteArray& getColor() { return _color; }
|
||||
QByteArray& getMaterial() { return _material; }
|
||||
|
||||
virtual bool hasOwnColors() const;
|
||||
virtual bool hasOwnMaterials() const;
|
||||
virtual QRgb getColorAt(const glm::vec3& point);
|
||||
virtual int getMaterialAt(const glm::vec3& point);
|
||||
virtual QVector<SharedObjectPointer>& getMaterials();
|
||||
|
||||
virtual bool contains(const glm::vec3& point);
|
||||
virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal);
|
||||
|
||||
private:
|
||||
|
||||
float _increment;
|
||||
int _width;
|
||||
float _heightScale;
|
||||
QByteArray _height;
|
||||
QByteArray _color;
|
||||
QByteArray _material;
|
||||
QVector<SharedObjectPointer> _materials;
|
||||
};
|
||||
|
||||
/// Base class for heightfield data blocks.
|
||||
class HeightfieldData : public DataBlock {
|
||||
public:
|
||||
|
||||
HeightfieldData(int width = 0);
|
||||
|
||||
int getWidth() const { return _width; }
|
||||
|
||||
protected:
|
||||
|
||||
int _width;
|
||||
};
|
||||
|
||||
typedef QExplicitlySharedDataPointer<HeightfieldHeight> HeightfieldHeightPointer;
|
||||
|
||||
/// A block of height data associated with a heightfield.
|
||||
class HeightfieldHeight : public HeightfieldData {
|
||||
public:
|
||||
|
||||
HeightfieldHeight(int width, const QVector<quint16>& contents);
|
||||
HeightfieldHeight(Bitstream& in, int bytes);
|
||||
HeightfieldHeight(Bitstream& in, int bytes, const HeightfieldHeightPointer& reference);
|
||||
|
||||
const QVector<quint16>& getContents() const { return _contents; }
|
||||
|
||||
void write(Bitstream& out);
|
||||
void writeDelta(Bitstream& out, const HeightfieldHeightPointer& reference);
|
||||
|
||||
private:
|
||||
|
||||
QVector<quint16> _contents;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(HeightfieldHeightPointer)
|
||||
|
||||
Bitstream& operator<<(Bitstream& out, const HeightfieldHeightPointer& value);
|
||||
Bitstream& operator>>(Bitstream& in, HeightfieldHeightPointer& value);
|
||||
|
||||
template<> void Bitstream::writeRawDelta(const HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference);
|
||||
template<> void Bitstream::readRawDelta(HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference);
|
||||
|
||||
typedef QExplicitlySharedDataPointer<HeightfieldColor> HeightfieldColorPointer;
|
||||
|
||||
/// A block of color data associated with a heightfield.
|
||||
class HeightfieldColor : public HeightfieldData {
|
||||
public:
|
||||
|
||||
HeightfieldColor(int width, const QByteArray& contents);
|
||||
HeightfieldColor(Bitstream& in, int bytes);
|
||||
HeightfieldColor(Bitstream& in, int bytes, const HeightfieldColorPointer& reference);
|
||||
|
||||
const QByteArray& getContents() const { return _contents; }
|
||||
|
||||
void write(Bitstream& out);
|
||||
void writeDelta(Bitstream& out, const HeightfieldColorPointer& reference);
|
||||
|
||||
private:
|
||||
|
||||
QByteArray _contents;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(HeightfieldColorPointer)
|
||||
|
||||
Bitstream& operator<<(Bitstream& out, const HeightfieldColorPointer& value);
|
||||
Bitstream& operator>>(Bitstream& in, HeightfieldColorPointer& value);
|
||||
|
||||
template<> void Bitstream::writeRawDelta(const HeightfieldColorPointer& value, const HeightfieldColorPointer& reference);
|
||||
template<> void Bitstream::readRawDelta(HeightfieldColorPointer& value, const HeightfieldColorPointer& reference);
|
||||
|
||||
typedef QExplicitlySharedDataPointer<HeightfieldMaterial> HeightfieldMaterialPointer;
|
||||
|
||||
/// A block of material data associated with a heightfield.
|
||||
class HeightfieldMaterial : public HeightfieldData {
|
||||
public:
|
||||
|
||||
HeightfieldMaterial(int width, const QByteArray& contents, const QVector<SharedObjectPointer>& materials);
|
||||
HeightfieldMaterial(Bitstream& in, int bytes);
|
||||
HeightfieldMaterial(Bitstream& in, int bytes, const HeightfieldMaterialPointer& reference);
|
||||
|
||||
const QByteArray& getContents() const { return _contents; }
|
||||
const QVector<SharedObjectPointer>& getMaterials() const { return _materials; }
|
||||
|
||||
void write(Bitstream& out);
|
||||
void writeDelta(Bitstream& out, const HeightfieldMaterialPointer& reference);
|
||||
|
||||
private:
|
||||
|
||||
QByteArray _contents;
|
||||
QVector<SharedObjectPointer> _materials;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(HeightfieldMaterialPointer)
|
||||
|
||||
Bitstream& operator<<(Bitstream& out, const HeightfieldMaterialPointer& value);
|
||||
Bitstream& operator>>(Bitstream& in, HeightfieldMaterialPointer& value);
|
||||
|
||||
template<> void Bitstream::writeRawDelta(const HeightfieldMaterialPointer& value, const HeightfieldMaterialPointer& reference);
|
||||
template<> void Bitstream::readRawDelta(HeightfieldMaterialPointer& value, const HeightfieldMaterialPointer& reference);
|
||||
|
||||
/// A heightfield represented as a spanner.
|
||||
class Heightfield : public Transformable {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(float aspectY MEMBER _aspectY WRITE setAspectY NOTIFY aspectYChanged)
|
||||
Q_PROPERTY(float aspectZ MEMBER _aspectZ WRITE setAspectZ NOTIFY aspectZChanged)
|
||||
Q_PROPERTY(HeightfieldHeightPointer height MEMBER _height WRITE setHeight NOTIFY heightChanged)
|
||||
Q_PROPERTY(HeightfieldColorPointer color MEMBER _color WRITE setColor NOTIFY colorChanged)
|
||||
Q_PROPERTY(HeightfieldMaterialPointer material MEMBER _material WRITE setMaterial NOTIFY materialChanged)
|
||||
|
||||
public:
|
||||
|
||||
Q_INVOKABLE Heightfield();
|
||||
|
||||
void setAspectY(float aspectY);
|
||||
float getAspectY() const { return _aspectY; }
|
||||
|
||||
void setAspectZ(float aspectZ);
|
||||
float getAspectZ() const { return _aspectZ; }
|
||||
|
||||
void setHeight(const HeightfieldHeightPointer& height);
|
||||
const HeightfieldHeightPointer& getHeight() const { return _height; }
|
||||
|
||||
void setColor(const HeightfieldColorPointer& color);
|
||||
const HeightfieldColorPointer& getColor() const { return _color; }
|
||||
|
||||
void setMaterial(const HeightfieldMaterialPointer& material);
|
||||
const HeightfieldMaterialPointer& getMaterial() const { return _material; }
|
||||
|
||||
signals:
|
||||
|
||||
void aspectYChanged(float aspectY);
|
||||
void aspectZChanged(float aspectZ);
|
||||
void heightChanged(const HeightfieldHeightPointer& height);
|
||||
void colorChanged(const HeightfieldColorPointer& color);
|
||||
void materialChanged(const HeightfieldMaterialPointer& material);
|
||||
|
||||
protected:
|
||||
|
||||
virtual QByteArray getRendererClassName() const;
|
||||
|
||||
private slots:
|
||||
|
||||
void updateBounds();
|
||||
|
||||
private:
|
||||
|
||||
float _aspectY;
|
||||
float _aspectZ;
|
||||
HeightfieldHeightPointer _height;
|
||||
HeightfieldColorPointer _color;
|
||||
HeightfieldMaterialPointer _material;
|
||||
};
|
||||
|
||||
#endif // hifi_Spanner_h
|
|
@ -81,7 +81,7 @@ PacketVersion versionForPacketType(PacketType type) {
|
|||
case PacketTypeAudioStreamStats:
|
||||
return 1;
|
||||
case PacketTypeMetavoxelData:
|
||||
return 8;
|
||||
return 9;
|
||||
case PacketTypeVoxelData:
|
||||
return VERSION_VOXELS_HAS_FILE_BREAKS;
|
||||
default:
|
||||
|
|
Loading…
Reference in a new issue