diff --git a/CMakeLists.txt b/CMakeLists.txt index b421040a50..679c0e260e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,4 +67,3 @@ add_subdirectory(ice-server) add_subdirectory(interface) add_subdirectory(tests) add_subdirectory(tools) -add_subdirectory(voxel-edit) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 32447d1d61..c3ec321c77 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -401,8 +401,8 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData* l if (distanceBetween < RADIUS_OF_HEAD) { // Diminish effect if source would be inside head - penumbraFilterGainL += (1.f - penumbraFilterGainL) * (1.f - distanceBetween / RADIUS_OF_HEAD); - penumbraFilterGainR += (1.f - penumbraFilterGainR) * (1.f - distanceBetween / RADIUS_OF_HEAD); + penumbraFilterGainL += (1.0f - penumbraFilterGainL) * (1.0f - distanceBetween / RADIUS_OF_HEAD); + penumbraFilterGainR += (1.0f - penumbraFilterGainR) * (1.0f - distanceBetween / RADIUS_OF_HEAD); } bool wantDebug = false; diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 3ec7c5cfbf..3e24457b1b 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -146,11 +146,11 @@ void AvatarMixer::broadcastAvatarData() { float distanceToAvatar = glm::length(myPosition - otherPosition); // The full rate distance is the distance at which EVERY update will be sent for this avatar // at a distance of twice the full rate distance, there will be a 50% chance of sending this avatar's update - const float FULL_RATE_DISTANCE = 2.f; + const float FULL_RATE_DISTANCE = 2.0f; // Decide whether to send this avatar's data based on it's distance from us if ((_performanceThrottlingRatio == 0 || randFloat() < (1.0f - _performanceThrottlingRatio)) - && (distanceToAvatar == 0.f || randFloat() < FULL_RATE_DISTANCE / distanceToAvatar)) { + && (distanceToAvatar == 0.0f || randFloat() < FULL_RATE_DISTANCE / distanceToAvatar)) { QByteArray avatarByteArray; avatarByteArray.append(otherNode->getUUID().toRfc4122()); avatarByteArray.append(otherAvatar.toByteArray()); diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index 81e86dbf11..314ffb28e4 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -9,7 +9,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include +#include #include #include #include @@ -33,14 +35,16 @@ void MetavoxelServer::applyEdit(const MetavoxelEditMessage& edit) { setData(data); } -void MetavoxelServer::setData(const MetavoxelData& data) { +void MetavoxelServer::setData(const MetavoxelData& data, bool loaded) { if (_data == data) { return; } emit dataChanged(_data = data); - if (!_savedDataInitialized) { + if (loaded) { _savedData = data; + + } else if (!_savedDataInitialized) { _savedDataInitialized = true; // start the save timer @@ -304,38 +308,58 @@ MetavoxelPersister::MetavoxelPersister(MetavoxelServer* server) : _server(server) { } -const char* SAVE_FILE = "metavoxels.dat"; +const char* SAVE_FILE = "/resources/metavoxels.dat"; + +const int FILE_MAGIC = 0xDADAFACE; +const int FILE_VERSION = 1; void MetavoxelPersister::load() { - QFile file(SAVE_FILE); + QString path = QCoreApplication::applicationDirPath() + SAVE_FILE; + QFile file(path); if (!file.exists()) { return; } MetavoxelData data; { - QDebug debug = qDebug() << "Reading from" << SAVE_FILE << "..."; + QDebug debug = qDebug() << "Reading from" << path << "..."; file.open(QIODevice::ReadOnly); QDataStream inStream(&file); Bitstream in(inStream); + int magic, version; + in >> magic; + if (magic != FILE_MAGIC) { + debug << "wrong file magic: " << magic; + return; + } + in >> version; + if (version != FILE_VERSION) { + debug << "wrong file version: " << version; + return; + } try { in >> data; } catch (const BitstreamException& e) { debug << "failed, " << e.getDescription(); return; } - QMetaObject::invokeMethod(_server, "setData", Q_ARG(const MetavoxelData&, data)); + QMetaObject::invokeMethod(_server, "setData", Q_ARG(const MetavoxelData&, data), Q_ARG(bool, true)); debug << "done."; } data.dumpStats(); } void MetavoxelPersister::save(const MetavoxelData& data) { - QDebug debug = qDebug() << "Writing to" << SAVE_FILE << "..."; - QSaveFile file(SAVE_FILE); + QString path = QCoreApplication::applicationDirPath() + SAVE_FILE; + QDir directory = QFileInfo(path).dir(); + if (!directory.exists()) { + directory.mkpath("."); + } + QDebug debug = qDebug() << "Writing to" << path << "..."; + QSaveFile file(path); file.open(QIODevice::WriteOnly); QDataStream outStream(&file); Bitstream out(outStream); - out << data; + out << FILE_MAGIC << FILE_VERSION << data; out.flush(); file.commit(); debug << "done."; diff --git a/assignment-client/src/metavoxels/MetavoxelServer.h b/assignment-client/src/metavoxels/MetavoxelServer.h index a4facb5426..2d1529b1b1 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.h +++ b/assignment-client/src/metavoxels/MetavoxelServer.h @@ -36,7 +36,7 @@ public: const MetavoxelData& getData() const { return _data; } - Q_INVOKABLE void setData(const MetavoxelData& data); + Q_INVOKABLE void setData(const MetavoxelData& data, bool loaded = false); virtual void run(); diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 21e18e41ba..a2d04887dc 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -700,8 +700,8 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url statsString += "\r\n"; const char* memoryScaleLabel; - const float MEGABYTES = 1000000.f; - const float GIGABYTES = 1000000000.f; + const float MEGABYTES = 1000000.0f; + const float GIGABYTES = 1000000000.0f; float memoryScale; if (OctreeElement::getTotalMemoryUsage() / MEGABYTES < 1000.0f) { memoryScaleLabel = "MB"; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index dbaaca43fa..02f17bc502 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1409,6 +1409,16 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url return false; } + // check if this is a request for our domain ID + const QString URI_ID = "/id"; + if (connection->requestOperation() == QNetworkAccessManager::GetOperation + && url.path() == URI_ID) { + QUuid domainID = LimitedNodeList::getInstance()->getSessionUUID(); + + connection->respond(HTTPConnection::StatusCode200, uuidStringWithoutCurlyBraces(domainID).toLocal8Bit()); + return true; + } + // all requests below require a cookie to prove authentication so check that first if (!isAuthenticatedRequest(connection, url)) { // this is not an authenticated request diff --git a/examples/butterflies.js b/examples/butterflies.js index e48095c345..8055e5b7d9 100644 --- a/examples/butterflies.js +++ b/examples/butterflies.js @@ -6,7 +6,7 @@ // Modified by Brad Hefta-Gaub to use Entities on Sept. 3, 2014 // Copyright 2014 High Fidelity, Inc. // -// This sample script creates a swarm of butterfly entities that fly around the avatar. +// This sample script creates a swarm of butterfly entities that fly around your avatar. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -40,17 +40,17 @@ function vInterpolate(a, b, fraction) { var startTimeInSeconds = new Date().getTime() / 1000; -var NATURAL_SIZE_OF_BUTTERFLY = { x: 9.512, y: 4.427, z: 1.169 }; +var NATURAL_SIZE_OF_BUTTERFLY = { x: 1.76, y: 0.825, z: 0.20 }; var lifeTime = 600; // lifetime of the butterflies in seconds -var range = 1.0; // Over what distance in meters do you want the flock to fly around +var range = 3.0; // Over what distance in meters do you want the flock to fly around var frame = 0; var CHANCE_OF_MOVING = 0.9; -var BUTTERFLY_GRAVITY = 0;//-0.06; -var BUTTERFLY_FLAP_SPEED = 1.0; +var BUTTERFLY_GRAVITY = 0; +var BUTTERFLY_FLAP_SPEED = 0.5; var BUTTERFLY_VELOCITY = 0.55; var DISTANCE_IN_FRONT_OF_ME = 1.5; -var DISTANCE_ABOVE_ME = 1.0; +var DISTANCE_ABOVE_ME = 1.5; var flockPosition = Vec3.sum(MyAvatar.position,Vec3.sum( Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_ABOVE_ME), Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_IN_FRONT_OF_ME))); @@ -81,11 +81,11 @@ function addButterfly() { var color = { red: 100, green: 100, blue: 100 }; var size = 0; - var minSize = 0.06; - var randomSize = 0.2; - var maxSize = minSize + randomSize; + var MINSIZE = 0.06; + var RANGESIZE = 0.2; + var maxSize = MINSIZE + RANGESIZE; - size = 0.06 + Math.random() * 0.2; + size = MINSIZE + Math.random() * RANGESIZE; var dimensions = Vec3.multiply(NATURAL_SIZE_OF_BUTTERFLY, (size / maxSize)); @@ -103,11 +103,10 @@ function addButterfly() { dimensions: dimensions, color: color, rotation: rotation, - animationURL: "http://business.ozblog.me/objects/butterfly/newButterfly2.fbx", + animationURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/models/content/butterfly/butterfly.fbx", animationIsPlaying: true, - modelURL: "http://business.ozblog.me/objects/butterfly/newButterfly2.fbx" + modelURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/models/content/butterfly/butterfly.fbx" }; - properties.position.z = properties.position.z+1; butterflies.push(new defineButterfly(Entities.addEntity(properties), properties.position)); } @@ -121,17 +120,15 @@ function updateButterflies(deltaTime) { // Check to see if we've been running long enough that our butterflies are dead var nowTimeInSeconds = new Date().getTime() / 1000; if ((nowTimeInSeconds - startTimeInSeconds) >= lifeTime) { - // print("our butterflies are dying, stop our script"); Script.stop(); return; } frame++; - // Only update every third frame + // Only update every third frame because we don't need to do it too quickly if ((frame % 3) == 0) { - flockPosition = Vec3.sum(MyAvatar.position,Vec3.sum( - Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_ABOVE_ME), - Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_IN_FRONT_OF_ME))); + flockPosition = Vec3.sum(MyAvatar.position,Vec3.sum(Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_ABOVE_ME), + Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_IN_FRONT_OF_ME))); // Update all the butterflies for (var i = 0; i < numButterflies; i++) { diff --git a/examples/drumStick.js b/examples/drumStick.js index 463b653e5f..88ffa36c50 100644 --- a/examples/drumStick.js +++ b/examples/drumStick.js @@ -64,9 +64,7 @@ function checkSticks(deltaTime) { if ((palmVelocity.y > 0.0) || (speed < STOP_SPEED)) { state[palm] = 0; - var options = { - position: Controller.getSpatialControlPosition(palm * 2 + 1); - } + var options = { position: Controller.getSpatialControlPosition(palm * 2 + 1) }; if (strokeSpeed[palm] > 1.0) { strokeSpeed[palm] = 1.0; } options.volume = strokeSpeed[palm]; diff --git a/examples/entityScripts/inspect.js b/examples/entityScripts/inspect.js new file mode 100644 index 0000000000..abfb445a17 --- /dev/null +++ b/examples/entityScripts/inspect.js @@ -0,0 +1,93 @@ +// +// inspect.js +// examples/entityScripts +// +// If you attach this script to an object, you can pick it up and look at it by clicking +// and holding/dragging. When you release, the object will return to it's original location. +// +// Created by Philip Rosedale on November 21, 2014 +// 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 +// +(function(){ + this.entityID = null; + this.properties = null; + this.originalPosition = null; + this.originalRotation = null; + this.radius = null; + this.lastMouseX = null; + this.lastMouseY = null; + this.sound = null; + this.isHolding = null; + + this.preload = function(entityID) { + this.updateProperties(entityID); // All callbacks start by updating the properties + this.sound = SoundCache.getSound("http://public.highfidelity.io/sounds/Footsteps/FootstepW3Left-12db.wav"); + } + + // Play drop sound + this.playSound = function() { + if (this.sound && this.sound.downloaded) { + Audio.playSound(this.sound, { position: this.properties.position }); + } + } + // All callbacks start by updating the properties + this.updateProperties = function(entityID) { + if (this.entityID === null || !this.entityID.isKnownID) { + this.entityID = Entities.identifyEntity(entityID); + } + this.properties = Entities.getEntityProperties(this.entityID); + }; + + this.clickDownOnEntity = function(entityID, mouseEvent) { + this.updateProperties(entityID); + + // record the old location of this object + this.originalPosition = this.properties.position; + this.originalRotation = this.properties.rotation; + this.radius = Vec3.length(this.properties.dimensions) / 2.0; + this.lastMouseX = mouseEvent.x; + this.lastMouseY = mouseEvent.y; + + var newPosition = Vec3.sum(Camera.position, Vec3.multiply(Quat.getFront(Camera.getOrientation()), this.radius * 3.0)); + // Place object in front of me + Entities.editEntity(this.entityID, { position: newPosition }); + this.isHolding = true; + this.playSound(); + }; + this.holdingClickOnEntity = function(entityID, mouseEvent) { + this.updateProperties(entityID); + if (mouseEvent.x != this.lastMouseX) { + var newRotation = Quat.multiply(Quat.fromPitchYawRollDegrees(0, mouseEvent.x - this.lastMouseX, 0), this.properties.rotation); + + this.lastMouseX = mouseEvent.x; + Entities.editEntity(this.entityID, { rotation: newRotation }); + } + if (mouseEvent.y != this.lastMouseY) { + var newRotation = Quat.multiply(Quat.fromPitchYawRollDegrees(mouseEvent.y - this.lastMouseY, 0, 0), this.properties.rotation); + this.lastMouseY = mouseEvent.y; + Entities.editEntity(this.entityID, { rotation: newRotation }); + } + }; + + this.returnItem = function(entityID) { + this.updateProperties(entityID); + Entities.editEntity(this.entityID, { position: this.originalPosition, + rotation: this.originalRotation }); + this.isHolding = false; + this.playSound(); + }; + + this.clickReleaseOnEntity = function(entityID, mouseEvent) { + this.returnItem(entityID); + }; + + this.unload = function(entityID) { + if (this.isHolding) { + this.returnItem(entityID); + } + }; + +}) diff --git a/examples/entityScripts/sitOnEntity.js b/examples/entityScripts/sitOnEntity.js new file mode 100644 index 0000000000..d5c4fa9c52 --- /dev/null +++ b/examples/entityScripts/sitOnEntity.js @@ -0,0 +1,372 @@ +// +// sitOnEntity.js +// examples/entityScripts +// +// Created by Brad Hefta-Gaub on 11/1/14. +// Copyright 2014 High Fidelity, Inc. +// +// This is an example of an entity script for sitting. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function(){ + + this.entityID = null; + this.properties = null; + this.standUpButton = null; + this.indicatorsAdded = false; + this.indicator = new Array(); + + + var buttonImageUrl = "https://worklist-prod.s3.amazonaws.com/attachment/0aca88e1-9bd8-5c1d.svg"; + var windowDimensions = Controller.getViewportDimensions(); + var buttonWidth = 37; + var buttonHeight = 46; + var buttonPadding = 50; // inside the normal toolbar position + var buttonPositionX = windowDimensions.x - buttonPadding - buttonWidth; + var buttonPositionY = (windowDimensions.y - buttonHeight) / 2 - (buttonHeight + buttonPadding); + + var passedTime = 0.0; + var startPosition = null; + var startRotation = null; + var animationLenght = 2.0; + + var avatarOldPosition = { x: 0, y: 0, z: 0 }; + + var sittingSettingsHandle = "SitJsSittingPosition"; + var sitting = Settings.getValue(sittingSettingsHandle, false) == "true"; + print("Original sitting status: " + sitting); + var frame = 0; + + var seat = new Object(); + var hiddingSeats = false; + + // This is the pose we would like to end up + var pose = [ + {joint:"RightUpLeg", rotation: {x:100.0, y:15.0, z:0.0}}, + {joint:"RightLeg", rotation: {x:-130.0, y:15.0, z:0.0}}, + {joint:"RightFoot", rotation: {x:30, y:15.0, z:0.0}}, + {joint:"LeftUpLeg", rotation: {x:100.0, y:-15.0, z:0.0}}, + {joint:"LeftLeg", rotation: {x:-130.0, y:-15.0, z:0.0}}, + {joint:"LeftFoot", rotation: {x:30, y:15.0, z:0.0}} + ]; + + var startPoseAndTransition = []; + + function storeStartPoseAndTransition() { + for (var i = 0; i < pose.length; i++){ + var startRotation = Quat.safeEulerAngles(MyAvatar.getJointRotation(pose[i].joint)); + var transitionVector = Vec3.subtract( pose[i].rotation, startRotation ); + startPoseAndTransition.push({joint: pose[i].joint, start: startRotation, transition: transitionVector}); + } + } + + function updateJoints(factor){ + for (var i = 0; i < startPoseAndTransition.length; i++){ + var scaledTransition = Vec3.multiply(startPoseAndTransition[i].transition, factor); + var rotation = Vec3.sum(startPoseAndTransition[i].start, scaledTransition); + MyAvatar.setJointData(startPoseAndTransition[i].joint, Quat.fromVec3Degrees( rotation )); + } + } + + var sittingDownAnimation = function(deltaTime) { + + passedTime += deltaTime; + var factor = passedTime/animationLenght; + + if ( passedTime <= animationLenght ) { + updateJoints(factor); + + var pos = { x: startPosition.x - 0.3 * factor, y: startPosition.y - 0.5 * factor, z: startPosition.z}; + MyAvatar.position = pos; + } else { + Script.update.disconnect(sittingDownAnimation); + if (seat.model) { + MyAvatar.setModelReferential(seat.model.id); + } + } + } + + var standingUpAnimation = function(deltaTime) { + + passedTime += deltaTime; + var factor = 1 - passedTime/animationLenght; + + if ( passedTime <= animationLenght ) { + + updateJoints(factor); + + var pos = { x: startPosition.x + 0.3 * (passedTime/animationLenght), y: startPosition.y + 0.5 * (passedTime/animationLenght), z: startPosition.z}; + MyAvatar.position = pos; + } else { + Script.update.disconnect(standingUpAnimation); + + } + } + + var externalThis = this; + + var goToSeatAnimation = function(deltaTime) { + passedTime += deltaTime; + var factor = passedTime/animationLenght; + + if (passedTime <= animationLenght) { + var targetPosition = Vec3.sum(seat.position, { x: 0.3, y: 0.5, z: 0 }); + MyAvatar.position = Vec3.sum(Vec3.multiply(startPosition, 1 - factor), Vec3.multiply(targetPosition, factor)); + } else if (passedTime <= 2 * animationLenght) { + //Quat.print("MyAvatar: ", MyAvatar.orientation); + //Quat.print("Seat: ", seat.rotation); + MyAvatar.orientation = Quat.mix(startRotation, seat.rotation, factor - 1); + } else { + Script.update.disconnect(goToSeatAnimation); + externalThis.sitDown(); + externalThis.showIndicators(false); + } + } + + var globalMouseClick = function(event) { + var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + if (clickedOverlay == externalThis.standUpButton) { + seat.model = null; + externalThis.standUp(); + Controller.mousePressEvent.disconnect(globalMouseClick); + } + }; + + this.sitDown = function() { + sitting = true; + Settings.setValue(sittingSettingsHandle, sitting); + print("sitDown sitting status: " + Settings.getValue(sittingSettingsHandle, false)); + passedTime = 0.0; + startPosition = MyAvatar.position; + storeStartPoseAndTransition(); + try { + Script.update.disconnect(standingUpAnimation); + } catch(e){ + // no need to handle. if it wasn't connected no harm done + } + Script.update.connect(sittingDownAnimation); + Overlays.editOverlay(this.standUpButton, { visible: true }); + Controller.mousePressEvent.connect(globalMouseClick); + } + + this.standUp = function() { + sitting = false; + Settings.setValue(sittingSettingsHandle, sitting); + print("standUp sitting status: " + Settings.getValue(sittingSettingsHandle, false)); + passedTime = 0.0; + startPosition = MyAvatar.position; + MyAvatar.clearReferential(); + try{ + Script.update.disconnect(sittingDownAnimation); + } catch (e){} + Script.update.connect(standingUpAnimation); + Overlays.editOverlay(this.standUpButton, { visible: false }); + Controller.mousePressEvent.disconnect(globalMouseClick); + } + + function SeatIndicator(modelProperties, seatIndex) { + var halfDiagonal = Vec3.length(modelProperties.dimensions) / 2.0; + + this.position = Vec3.sum(modelProperties.position, + Vec3.multiply(Vec3.multiplyQbyV(modelProperties.rotation, modelProperties.sittingPoints[seatIndex].position), + halfDiagonal)); // hack + + this.orientation = Quat.multiply(modelProperties.rotation, + modelProperties.sittingPoints[seatIndex].rotation); + this.scale = MyAvatar.scale / 3; + + this.sphere = Overlays.addOverlay("billboard", { + subImage: { x: 0, y: buttonHeight, width: buttonWidth, height: buttonHeight}, + url: buttonImageUrl, + position: this.position, + scale: this.scale, + size: this.scale, + solid: true, + color: { red: 255, green: 255, blue: 255 }, + alpha: 0.8, + visible: true, + isFacingAvatar: true + }); + + this.show = function(doShow) { + Overlays.editOverlay(this.sphere, { visible: doShow }); + } + + this.update = function() { + Overlays.editOverlay(this.sphere, { + position: this.position, + size: this.scale + }); + } + + this.cleanup = function() { + Overlays.deleteOverlay(this.sphere); + } + } + + function update(deltaTime){ + var newWindowDimensions = Controller.getViewportDimensions(); + if( newWindowDimensions.x != windowDimensions.x || newWindowDimensions.y != windowDimensions.y ){ + windowDimensions = newWindowDimensions; + var newX = windowDimensions.x - buttonPadding - buttonWidth; + var newY = (windowDimensions.y - buttonHeight) / 2 ; + Overlays.editOverlay( this.standUpButton, {x: newX, y: newY} ); + } + + // For a weird reason avatar joint don't update till the 10th frame + // Set the update frame to 20 to be safe + var UPDATE_FRAME = 20; + if (frame <= UPDATE_FRAME) { + if (frame == UPDATE_FRAME) { + if (sitting == true) { + print("Was seated: " + sitting); + storeStartPoseAndTransition(); + updateJoints(1.0); + } + } + frame++; + } + } + + this.addIndicators = function() { + if (!this.indicatorsAdded) { + if (this.properties.sittingPoints.length > 0) { + for (var i = 0; i < this.properties.sittingPoints.length; ++i) { + this.indicator[i] = new SeatIndicator(this.properties, i); + } + this.indicatorsAdded = true; + } + } + } + + this.removeIndicators = function() { + for (var i = 0; i < this.properties.sittingPoints.length; ++i) { + this.indicator[i].cleanup(); + } + } + + this.showIndicators = function(doShow) { + this.addIndicators(); + if (this.indicatorsAdded) { + for (var i = 0; i < this.properties.sittingPoints.length; ++i) { + this.indicator[i].show(doShow); + } + } + hiddingSeats = !doShow; + } + + function raySphereIntersection(origin, direction, center, radius) { + var A = origin; + var B = Vec3.normalize(direction); + var P = center; + + var x = Vec3.dot(Vec3.subtract(P, A), B); + var X = Vec3.sum(A, Vec3.multiply(B, x)); + var d = Vec3.length(Vec3.subtract(P, X)); + + return (x > 0 && d <= radius); + } + + this.cleanup = function() { + this.standUp(); + MyAvatar.clearReferential(); + for (var i = 0; i < pose.length; i++){ + MyAvatar.clearJointData(pose[i].joint); + } + Overlays.deleteOverlay(this.standUpButton); + for (var i = 0; i < this.indicator.length; ++i) { + this.indicator[i].cleanup(); + } + }; + + + this.createStandupButton = function() { + this.standUpButton = Overlays.addOverlay("image", { + x: buttonPositionX, y: buttonPositionY, width: buttonWidth, height: buttonHeight, + subImage: { x: buttonWidth, y: buttonHeight, width: buttonWidth, height: buttonHeight}, + imageURL: buttonImageUrl, + visible: false, + alpha: 1.0 + }); + }; + + this.handleClickEvent = function(event) { + var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + + if (clickedOverlay == this.standUpButton) { + seat.model = null; + this.standUp(); + } else { + this.addIndicators(); + if (this.indicatorsAdded) { + var pickRay = Camera.computePickRay(event.x, event.y); + + var clickedOnSeat = false; + + for (var i = 0; i < this.properties.sittingPoints.length; ++i) { + if (raySphereIntersection(pickRay.origin, + pickRay.direction, + this.indicator[i].position, + this.indicator[i].scale / 2)) { + clickedOnSeat = true; + seat.model = this.entityID; // ?? + seat.position = this.indicator[i].position; + seat.rotation = this.indicator[i].orientation; + } + } + + if (clickedOnSeat) { + passedTime = 0.0; + startPosition = MyAvatar.position; + startRotation = MyAvatar.orientation; + try{ Script.update.disconnect(standingUpAnimation); } catch(e){} + try{ Script.update.disconnect(sittingDownAnimation); } catch(e){} + Script.update.connect(goToSeatAnimation); + } + } + } + }; + + + // All callbacks start by updating the properties + this.updateProperties = function(entityID) { + if (this.entityID === null || !this.entityID.isKnownID) { + this.entityID = Entities.identifyEntity(entityID); + } + this.properties = Entities.getEntityProperties(this.entityID); + }; + + this.unload = function(entityID) { + this.cleanup(); + Script.update.disconnect(update); + }; + + this.preload = function(entityID) { + this.updateProperties(entityID); // All callbacks start by updating the properties + this.createStandupButton(); + Script.update.connect(update); + }; + + + this.hoverOverEntity = function(entityID, mouseEvent) { + this.updateProperties(entityID); // All callbacks start by updating the properties + this.showIndicators(true); + }; + this.hoverLeaveEntity = function(entityID, mouseEvent) { + this.updateProperties(entityID); // All callbacks start by updating the properties + this.showIndicators(false); + }; + + this.clickDownOnEntity = function(entityID, mouseEvent) { + this.updateProperties(entityID); // All callbacks start by updating the properties + this.handleClickEvent(mouseEvent); + }; + + this.clickReleaseOnEntity = function(entityID, mouseEvent) { + this.updateProperties(entityID); // All callbacks start by updating the properties + }; + +}) \ No newline at end of file diff --git a/examples/flockingBirds.js b/examples/flockingBirds.js index 92a7b75f52..fd59b20c48 100644 --- a/examples/flockingBirds.js +++ b/examples/flockingBirds.js @@ -26,7 +26,7 @@ var birdsInFlock = 20; -var birdLifetime = 60; // 2 minutes +var birdLifetime = 300; // 1 minute var count=0; // iterations var enableFlyTowardPoints = true; // some birds have a point they want to fly to diff --git a/examples/headMove.js b/examples/headMove.js index 56f42984e4..df5c858b46 100644 --- a/examples/headMove.js +++ b/examples/headMove.js @@ -89,6 +89,7 @@ var WARP_SMOOTHING = 0.90; var WARP_START_TIME = 0.25; var WARP_START_DISTANCE = 2.5; var WARP_SENSITIVITY = 0.15; +var MAX_WARP_DISTANCE = 25.0; var fixedHeight = true; @@ -105,7 +106,7 @@ function updateWarp() { willMove = (keyDownTime > WARP_START_TIME); if (willMove) { - var distance = Math.exp(deltaPitch * WARP_SENSITIVITY) * WARP_START_DISTANCE; + var distance = Math.min(Math.exp(deltaPitch * WARP_SENSITIVITY) * WARP_START_DISTANCE, MAX_WARP_DISTANCE); var warpDirection = Vec3.normalize({ x: look.x, y: (fixedHeight ? 0 : look.y), z: look.z }); var startPosition = (watchAvatar ? Camera.getPosition(): MyAvatar.getEyePosition()); warpPosition = Vec3.mix(Vec3.sum(startPosition, Vec3.multiply(warpDirection, distance)), warpPosition, WARP_SMOOTHING); @@ -113,7 +114,7 @@ function updateWarp() { var cameraPosition; - if (!watchAvatar && willMove) { + if (!watchAvatar && willMove && (distance < WARP_START_DISTANCE * 0.5)) { pullBack(); watchAvatar = true; } diff --git a/examples/hotPlaces.js b/examples/hotPlaces.js new file mode 100644 index 0000000000..cb826837ae --- /dev/null +++ b/examples/hotPlaces.js @@ -0,0 +1,27 @@ +// +// hotPlaces.js +// +// Press the number keys to jump to different places in the metaverse! +// +// Created by Philip Rosedale on November 20, 2014 +// 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 + + +Controller.keyPressEvent.connect(function (event) { + if (event.text == "1") { + Window.location = "hifi://starchamber"; + } else if (event.text == "2") { + Window.location = "hifi://apartment"; + } else if (event.text == "3") { + Window.location = "hifi://rivenglen"; + } else if (event.text == "4") { + Window.location = "hifi://sanfrancisco"; + } else if (event.text == "5") { + Window.location = "hifi://porto"; + } else if (event.text == "6") { + Window.location = "hifi://hoo"; + } +}); diff --git a/examples/libraries/entityList.js b/examples/libraries/entityList.js index 65c06c8cea..eb01295d97 100644 --- a/examples/libraries/entityList.js +++ b/examples/libraries/entityList.js @@ -33,12 +33,11 @@ EntityListTool = function(opts) { var ids = data.entityIds; var entityIDs = []; for (var i = 0; i < ids.length; i++) { - var entityID = Entities.getEntityItemID(ids[i]); - if (entityID.isKnownID) { - entityIDs.push(entityID); - } else { - print("Tried to select invalid entity: " + ids[i]); - } + entityIDs.push({ + id: ids[i], + isKnownID: true, + creatorTokenID: 0, + }); } selectionManager.setSelections(entityIDs); if (data.focus) { diff --git a/examples/libraries/gridTool.js b/examples/libraries/gridTool.js index eb2a227248..de4fd7b8d4 100644 --- a/examples/libraries/gridTool.js +++ b/examples/libraries/gridTool.js @@ -53,11 +53,31 @@ Grid = function(opts) { } } - that.snapToGrid = function(position, majorOnly) { + that.snapToSurface = function(position, dimensions) { if (!snapToGrid) { return position; } + if (dimensions === undefined) { + dimensions = { x: 0, y: 0, z: 0 }; + } + + return { + x: position.x, + y: origin.y + (dimensions.y / 2), + z: position.z + }; + } + + that.snapToGrid = function(position, majorOnly, dimensions) { + if (!snapToGrid) { + return position; + } + + if (dimensions === undefined) { + dimensions = { x: 0, y: 0, z: 0 }; + } + var spacing = majorOnly ? (minorGridSpacing * majorGridEvery) : minorGridSpacing; position = Vec3.subtract(position, origin); @@ -66,7 +86,7 @@ Grid = function(opts) { position.y = Math.round(position.y / spacing) * spacing; position.z = Math.round(position.z / spacing) * spacing; - return Vec3.sum(position, origin); + return Vec3.sum(Vec3.sum(position, Vec3.multiply(0.5, dimensions)), origin); } that.snapToSpacing = function(delta, majorOnly) { diff --git a/examples/lobby.js b/examples/lobby.js index 437cfe40f3..9e454eccc9 100644 --- a/examples/lobby.js +++ b/examples/lobby.js @@ -14,18 +14,28 @@ Script.include("libraries/globals.js"); var panelWall = false; var orbShell = false; var reticle = false; +var descriptionText = false; +// used for formating the description text, in meters +var textWidth = 4; +var textHeight = .5; +var numberOfLines = 2; +var textMargin = 0.0625; +var lineHeight = (textHeight - (2 * textMargin)) / numberOfLines; + +var lastMouseMove = 0; +var IDLE_HOVER_TIME = 2000; // if you haven't moved the mouse in 2 seconds, and in HMD mode, then we use reticle for hover var avatarStickPosition = {}; -var orbNaturalExtentsMin = { x: -1230, y: -1223, z: -1210 }; -var orbNaturalExtentsMax = { x: 1230, y: 1229, z: 1210 }; -var panelsNaturalExtentsMin = { x: -1223, y: -348, z: 45 }; -var panelsNaturalExtentsMax = { x: 1223, y: 604, z: 1223 }; +var orbNaturalExtentsMin = { x: -1.230354, y: -1.22077, z: -1.210487 }; +var orbNaturalExtentsMax = { x: 1.230353, y: 1.229819, z: 1.210487 }; +var panelsNaturalExtentsMin = { x: -1.223182, y: -0.348487, z: 0.0451369 }; +var panelsNaturalExtentsMax = { x: 1.223039, y: 0.602978, z: 1.224298 }; var orbNaturalDimensions = Vec3.subtract(orbNaturalExtentsMax, orbNaturalExtentsMin); var panelsNaturalDimensions = Vec3.subtract(panelsNaturalExtentsMax, panelsNaturalExtentsMin); -var SCALING_FACTOR = 0.01; +var SCALING_FACTOR = 10; var orbDimensions = Vec3.multiply(orbNaturalDimensions, SCALING_FACTOR); var panelsDimensions = Vec3.multiply(panelsNaturalDimensions, SCALING_FACTOR); @@ -37,24 +47,33 @@ var panelsCenterShift = Vec3.subtract(panelsCenter, orbCenter); var ORB_SHIFT = { x: 0, y: -1.4, z: -0.8}; -var HELMET_ATTACHMENT_URL = HIFI_PUBLIC_BUCKET + "models/attachments/IronManMaskOnly.fbx" +var HELMET_ATTACHMENT_URL = HIFI_PUBLIC_BUCKET + "models/attachments/IronManMaskOnly.fbx" var droneSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/drone.stereo.raw") var currentDrone = null; var latinSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/latin.stereo.raw") var elevatorSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/elevator.stereo.raw") -var currentMusakInjector = null; +var currentMuzakInjector = null; var currentSound = null; -var inOculusMode = Menu.isOptionChecked("EnableVRMode"); +var inOculusMode = false; function reticlePosition() { var RETICLE_DISTANCE = 1; return Vec3.sum(Camera.position, Vec3.multiply(Quat.getFront(Camera.orientation), RETICLE_DISTANCE)); } +function textOverlayPosition() { + var TEXT_DISTANCE_OUT = 6; + var TEXT_DISTANCE_DOWN = -2; + return Vec3.sum(Vec3.sum(Camera.position, Vec3.multiply(Quat.getFront(Camera.orientation), TEXT_DISTANCE_OUT)), + Vec3.multiply(Quat.getUp(Camera.orientation), TEXT_DISTANCE_DOWN)); +} + + var MAX_NUM_PANELS = 21; +var DRONE_VOLUME = 0.3; function drawLobby() { if (!panelWall) { @@ -66,24 +85,46 @@ function drawLobby() { var orbPosition = Vec3.sum(Camera.position, Vec3.multiplyQbyV(towardsMe, ORB_SHIFT)); var panelWallProps = { - url: HIFI_PUBLIC_BUCKET + "models/sets/Lobby/LobbyPrototype/Lobby5_PanelsWithFrames.fbx", + url: HIFI_PUBLIC_BUCKET + "models/sets/Lobby/Lobby_v8/forStephen1/PanelWall2.fbx", position: Vec3.sum(orbPosition, Vec3.multiplyQbyV(towardsMe, panelsCenterShift)), rotation: towardsMe, - dimensions: panelsDimensions, + dimensions: panelsDimensions }; var orbShellProps = { - url: HIFI_PUBLIC_BUCKET + "models/sets/Lobby/LobbyPrototype/Lobby5_OrbNoFrames.fbx", + url: HIFI_PUBLIC_BUCKET + "models/sets/Lobby/Lobby_v8/forStephen1/LobbyShell1.fbx", position: orbPosition, rotation: towardsMe, dimensions: orbDimensions, ignoreRayIntersection: true }; + + var windowDimensions = Controller.getViewportDimensions(); + + var descriptionTextProps = { + position: textOverlayPosition(), + dimensions: { x: textWidth, y: textHeight }, + backgroundColor: { red: 0, green: 0, blue: 0}, + color: { red: 255, green: 255, blue: 255}, + topMargin: textMargin, + leftMargin: textMargin, + bottomMargin: textMargin, + rightMargin: textMargin, + text: "", + lineHeight: lineHeight, + alpha: 0.9, + ignoreRayIntersection: true, + visible: false, + isFacingAvatar: true + }; avatarStickPosition = MyAvatar.position; panelWall = Overlays.addOverlay("model", panelWallProps); orbShell = Overlays.addOverlay("model", orbShellProps); + descriptionText = Overlays.addOverlay("text3d", descriptionTextProps); + + inOculusMode = Menu.isOptionChecked("Enable VR Mode"); // for HMD wearers, create a reticle in center of screen if (inOculusMode) { @@ -103,10 +144,10 @@ function drawLobby() { MyAvatar.attach(HELMET_ATTACHMENT_URL, "Neck", {x: 0, y: 0, z: 0}, Quat.fromPitchYawRollDegrees(0, 0, 0), 1.15); // start the drone sound - currentDrone = Audio.playSound(droneSound, { stereo: true, loop: true, localOnly: true }); + currentDrone = Audio.playSound(droneSound, { stereo: true, loop: true, localOnly: true, volume: DRONE_VOLUME }); - // start one of our musak sounds - playRandomMusak(); + // start one of our muzak sounds + playRandomMuzak(); } } @@ -132,9 +173,9 @@ function changeLobbyTextures() { Overlays.editOverlay(panelWall, textureProp); } -var MUSAK_VOLUME = 0.5; +var MUZAK_VOLUME = 0.1; -function playNextMusak() { +function playNextMuzak() { if (panelWall) { if (currentSound == latinSound) { if (elevatorSound.downloaded) { @@ -146,11 +187,11 @@ function playNextMusak() { } } - currentMusakInjector = Audio.playSound(currentSound, { localOnly: true, volume: MUSAK_VOLUME }); + currentMuzakInjector = Audio.playSound(currentSound, { localOnly: true, volume: MUZAK_VOLUME }); } } -function playRandomMusak() { +function playRandomMuzak() { currentSound = null; if (latinSound.downloaded && elevatorSound.downloaded) { @@ -162,28 +203,29 @@ function playRandomMusak() { } if (currentSound) { - // pick a random number of seconds from 0-10 to offset the musak + // pick a random number of seconds from 0-10 to offset the muzak var secondOffset = Math.random() * 10; - currentMusakInjector = Audio.playSound(currentSound, { localOnly: true, secondOffset: secondOffset, volume: MUSAK_VOLUME }); + currentMuzakInjector = Audio.playSound(currentSound, { localOnly: true, secondOffset: secondOffset, volume: MUZAK_VOLUME }); } else { - currentMusakInjector = null; + currentMuzakInjector = null; } } function cleanupLobby() { - // for each of the 21 placeholder textures, set them back to default so the cached model doesn't have changed textures var panelTexturesReset = {}; panelTexturesReset["textures"] = {}; - - for (var j = 0; j < MAX_NUM_PANELS; j++) { + + for (var j = 0; j < MAX_NUM_PANELS; j++) { panelTexturesReset["textures"]["file" + (j + 1)] = HIFI_PUBLIC_BUCKET + "models/sets/Lobby/LobbyPrototype/Texture.jpg"; }; - + Overlays.editOverlay(panelWall, panelTexturesReset); Overlays.deleteOverlay(panelWall); Overlays.deleteOverlay(orbShell); + Overlays.deleteOverlay(descriptionText); + if (reticle) { Overlays.deleteOverlay(reticle); @@ -196,8 +238,8 @@ function cleanupLobby() { Audio.stopInjector(currentDrone); currentDrone = null; - Audio.stopInjector(currentMusakInjector); - currentMusakInjector = null; + Audio.stopInjector(currentMuzakInjector); + currentMuzakInjector = null; locations = {}; toggleEnvironmentRendering(true); @@ -207,14 +249,13 @@ function cleanupLobby() { function actionStartEvent(event) { if (panelWall) { - // we've got an action event and our panel wall is up // check if we hit a panel and if we should jump there var result = Overlays.findRayIntersection(event.actionRay); - if (result.intersects && result.overlayID == panelWall) { - + var panelName = result.extraInfo; + var panelStringIndex = panelName.indexOf("Panel"); if (panelStringIndex != -1) { var panelIndex = parseInt(panelName.slice(5)) - 1; @@ -242,7 +283,7 @@ function backStartEvent() { } } -var CLEANUP_EPSILON_DISTANCE = 0.025 +var CLEANUP_EPSILON_DISTANCE = 0.05; function maybeCleanupLobby() { if (panelWall && Vec3.length(Vec3.subtract(avatarStickPosition, MyAvatar.position)) > CLEANUP_EPSILON_DISTANCE) { @@ -257,6 +298,62 @@ function toggleEnvironmentRendering(shouldRender) { Menu.setIsOptionChecked("Avatars", shouldRender); } +function handleLookAt(pickRay) { + if (panelWall && descriptionText) { + // we've got an action event and our panel wall is up + // check if we hit a panel and if we should jump there + var result = Overlays.findRayIntersection(pickRay); + if (result.intersects && result.overlayID == panelWall) { + var panelName = result.extraInfo; + var panelStringIndex = panelName.indexOf("Panel"); + if (panelStringIndex != -1) { + var panelIndex = parseInt(panelName.slice(5)) - 1; + if (panelIndex < locations.length) { + var actionLocation = locations[panelIndex]; + + if (actionLocation.description == "") { + Overlays.editOverlay(descriptionText, { text: actionLocation.name, visible: true }); + } else { + // handle line wrapping + var allWords = actionLocation.description.split(" "); + var currentGoodLine = ""; + var currentTestLine = ""; + var formatedDescription = ""; + var wordsFormated = 0; + var currentTestWord = 0; + var wordsOnLine = 0; + while (wordsFormated < allWords.length) { + // first add the "next word" to the line and test it. + currentTestLine = currentGoodLine; + if (wordsOnLine > 0) { + currentTestLine += " " + allWords[currentTestWord]; + } else { + currentTestLine = allWords[currentTestWord]; + } + var lineLength = Overlays.textWidth(descriptionText, currentTestLine); + if (lineLength < textWidth || wordsOnLine == 0) { + wordsFormated++; + currentTestWord++; + wordsOnLine++; + currentGoodLine = currentTestLine; + } else { + formatedDescription += currentGoodLine + "\n"; + wordsOnLine = 0; + currentGoodLine = ""; + currentTestLine = ""; + } + } + formatedDescription += currentGoodLine; + Overlays.editOverlay(descriptionText, { text: formatedDescription, visible: true }); + } + } else { + Overlays.editOverlay(descriptionText, { text: "", visible: false }); + } + } + } + } +} + function update(deltaTime) { maybeCleanupLobby(); if (panelWall) { @@ -265,16 +362,35 @@ function update(deltaTime) { Overlays.editOverlay(reticle, { position: reticlePosition() }); + + var nowDate = new Date(); + var now = nowDate.getTime(); + if (now - lastMouseMove > IDLE_HOVER_TIME) { + var pickRay = Camera.computeViewPickRay(0.5, 0.5); + handleLookAt(pickRay); + } } - - // if the reticle is up then we may need to play the next musak - if (!Audio.isInjectorPlaying(currentMusakInjector)) { - playNextMusak(); + + Overlays.editOverlay(descriptionText, { position: textOverlayPosition() }); + + // if the reticle is up then we may need to play the next muzak + if (!Audio.isInjectorPlaying(currentMuzakInjector)) { + playNextMuzak(); } } } +function mouseMoveEvent(event) { + if (panelWall) { + var nowDate = new Date(); + lastMouseMove = nowDate.getTime(); + var pickRay = Camera.computePickRay(event.x, event.y); + handleLookAt(pickRay); + } +} + Controller.actionStartEvent.connect(actionStartEvent); Controller.backStartEvent.connect(backStartEvent); Script.update.connect(update); -Script.scriptEnding.connect(maybeCleanupLobby); \ No newline at end of file +Script.scriptEnding.connect(maybeCleanupLobby); +Controller.mouseMoveEvent.connect(mouseMoveEvent); diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js index 27a3e8a9d2..bc2d87259e 100644 --- a/examples/newEditEntities.js +++ b/examples/newEditEntities.js @@ -61,6 +61,12 @@ var DEFAULT_TEXT_DIMENSION_X = 1.0; var DEFAULT_TEXT_DIMENSION_Y = 1.0; var DEFAULT_TEXT_DIMENSION_Z = 0.01; +var DEFAULT_DIMENSIONS = { + x: DEFAULT_DIMENSION, + y: DEFAULT_DIMENSION, + z: DEFAULT_DIMENSION +}; + var MENU_INSPECT_TOOL_ENABLED = "Inspect Tool"; var MENU_EASE_ON_FOCUS = "Ease Orientation on Focus"; @@ -220,8 +226,8 @@ var toolBar = (function () { if (position.x > 0 && position.y > 0 && position.z > 0) { var entityId = Entities.addEntity({ type: "Model", - position: position, - dimensions: { x: DEFAULT_DIMENSION, y: DEFAULT_DIMENSION, z: DEFAULT_DIMENSION }, + position: grid.snapToSurface(grid.snapToGrid(position, false, DEFAULT_DIMENSIONS), DEFAULT_DIMENSIONS), + dimensions: DEFAULT_DIMENSIONS, modelURL: url }); print("Model added: " + url); @@ -345,8 +351,8 @@ var toolBar = (function () { if (position.x > 0 && position.y > 0 && position.z > 0) { Entities.addEntity({ type: "Box", - position: position, - dimensions: { x: DEFAULT_DIMENSION, y: DEFAULT_DIMENSION, z: DEFAULT_DIMENSION }, + position: grid.snapToSurface(grid.snapToGrid(position, false, DEFAULT_DIMENSIONS), DEFAULT_DIMENSIONS), + dimensions: DEFAULT_DIMENSIONS, color: { red: 255, green: 0, blue: 0 } }); @@ -362,8 +368,8 @@ var toolBar = (function () { if (position.x > 0 && position.y > 0 && position.z > 0) { Entities.addEntity({ type: "Sphere", - position: position, - dimensions: { x: DEFAULT_DIMENSION, y: DEFAULT_DIMENSION, z: DEFAULT_DIMENSION }, + position: grid.snapToSurface(grid.snapToGrid(position, false, DEFAULT_DIMENSIONS), DEFAULT_DIMENSIONS), + dimensions: DEFAULT_DIMENSIONS, color: { red: 255, green: 0, blue: 0 } }); } else { @@ -378,8 +384,8 @@ var toolBar = (function () { if (position.x > 0 && position.y > 0 && position.z > 0) { Entities.addEntity({ type: "Light", - position: position, - dimensions: { x: DEFAULT_DIMENSION, y: DEFAULT_DIMENSION, z: DEFAULT_DIMENSION }, + position: grid.snapToSurface(grid.snapToGrid(position, false, DEFAULT_DIMENSIONS), DEFAULT_DIMENSIONS), + dimensions: DEFAULT_DIMENSIONS, isSpotlight: false, diffuseColor: { red: 255, green: 255, blue: 255 }, ambientColor: { red: 255, green: 255, blue: 255 }, @@ -403,8 +409,8 @@ var toolBar = (function () { if (position.x > 0 && position.y > 0 && position.z > 0) { Entities.addEntity({ type: "Text", - position: position, - dimensions: { x: DEFAULT_TEXT_DIMENSION_X, y: DEFAULT_TEXT_DIMENSION_Y, z: DEFAULT_TEXT_DIMENSION_Z }, + position: grid.snapToSurface(grid.snapToGrid(position, false, DEFAULT_DIMENSIONS), DEFAULT_DIMENSIONS), + dimensions: DEFAULT_DIMENSIONS, backgroundColor: { red: 0, green: 0, blue: 0 }, textColor: { red: 255, green: 255, blue: 255 }, text: "some text", diff --git a/examples/notifications.js b/examples/notifications.js new file mode 100644 index 0000000000..16e9dd70b2 --- /dev/null +++ b/examples/notifications.js @@ -0,0 +1,377 @@ +// +// notifications.js +// Created by Adrian +// +// Adrian McCarlie 8-10-14 +// This script demonstrates on-screen overlay type notifications. +// 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 + +// This script demonstrates notifications created via a number of ways, such as: +// Simple key press alerts, which only depend on a key being pressed, +// dummy examples of this are "q", "w", "e", "r", and "SPACEBAR". +// actual working examples are "a" for left turn, "d" for right turn and Ctrl/s for snapshot. + +// System generated alerts such as users joining and leaving and chat messages which mention this user. +// System generated alerts which originate with a user interface event such as Window Resize, and Mic Mute/Unmute. +// Mic Mute/Unmute may appear to be a key press alert, but it actually gets the call from the system as mic is muted and unmuted, +// so the mic mute/unmute will also trigger the notification by clicking the Mic Mute button at the top of the screen. + + +// To add a new System notification type: +// +// 1. Set the Event Connector at the bottom of the script. +// example: +// GlobalServices.incomingMessage.connect(onIncomingMessage); +// +// 2. Create a new function to produce a text string, do not include new line returns. +// example: +// function onIncomingMessage(user, message) { +// //do stuff here; +// var text = "This is a notification"; +// wordWrap(text); +// } +// +// This new function must call wordWrap(text) if the length of message is longer than 42 chars or unknown. +// wordWrap() will format the text to fit the notifications overlay and send it to createNotification(text). +// If the message is 42 chars or less you should bypass wordWrap() and call createNotification() directly. + + +// To add a keypress driven notification: +// +// 1. Add a key to the keyPressEvent(key). +// 2. Declare a text string. +// 3. Call createNotifications(text) parsing the text. +// example: +// if (key.text == "a") { +// var noteString = "Turning to the Left"; +// createNotification(noteString); +// } + + +var width = 340.0; //width of notification overlay +var height = 40.0; // height of a single line notification overlay +var windowDimensions = Controller.getViewportDimensions(); // get the size of the interface window +var overlayLocationX = (windowDimensions.x - (width + 60.0));// positions window 60px from the right of the interface window +var buttonLocationX = overlayLocationX + (width - 28.0); +var locationY = 20.0; // position down from top of interface window +var topMargin = 13.0; +var leftMargin = 10.0; +var textColor = { red: 228, green: 228, blue: 228}; // text color +var backColor = { red: 38, green: 38, blue: 38}; // background color +var backgroundAlpha = 0; +var fontSize = 12.0; +var persistTime = 10.0; // time in seconds before notification fades +var clickedText = false; +var frame = 0; +var ourWidth = Window.innerWidth; +var ourHeight = Window.innerHeight; +var text = "placeholder"; +var last_users = GlobalServices.onlineUsers; +var users = []; +var ctrlIsPressed = false; +var ready = true; + +// When our script shuts down, we should clean up all of our overlays +function scriptEnding() { + for (i = 0; i < notifications.length; i++) { + Overlays.deleteOverlay(notifications[i]); + Overlays.deleteOverlay(buttons[i]); + } +} +Script.scriptEnding.connect(scriptEnding); + +var notifications = []; +var buttons = []; +var times = []; +var heights = []; +var myAlpha = []; +var arrays = []; + +// This function creates and sizes the overlays +function createNotification(text) { + var count = (text.match(/\n/g) || []).length; + var breakPoint = 43.0; // length when new line is added + var extraLine = 0; + var breaks = 0; + var height = 40.0; + var stack = 0; + if (text.length >= breakPoint) { + breaks = count; + } + var extraLine = breaks * 16.0; + for (i = 0; i < heights.length; i++) { + stack = stack + heights[i]; + } + var level = (stack + 20.0); + height = height + extraLine; + var overlayProperties = { + x: overlayLocationX, + y: level, + width: width, + height: height, + color: textColor, + backgroundColor: backColor, + alpha: backgroundAlpha, + topMargin: topMargin, + leftMargin: leftMargin, + font: {size: fontSize}, + text: text, + }; + var bLevel = level + 12.0; + var buttonProperties = { + x: buttonLocationX, + y: bLevel, + width: 15.0, + height: 15.0, + subImage: { x: 0, y: 0, width: 10, height: 10 }, + imageURL: "http://hifi-public.s3.amazonaws.com/images/close-small-light.svg", + color: { red: 255, green: 255, blue: 255}, + visible: true, + alpha: backgroundAlpha, + }; + + Notify(overlayProperties, buttonProperties, height); + +} + +// Pushes data to each array and sets up data for 2nd dimension array +// to handle auxiliary data not carried by the overlay class +// specifically notification "heights", "times" of creation, and . +function Notify(notice, button, height){ + + notifications.push((Overlays.addOverlay("text", notice))); + buttons.push((Overlays.addOverlay("image",button))); + times.push(new Date().getTime() / 1000); + height = height + 1.0; + heights.push(height); + myAlpha.push(0); + var last = notifications.length - 1; + createArrays(notifications[last], buttons[last], times[last], heights[last], myAlpha[last]); + fadeIn(notifications[last], buttons[last]) +} + +function fadeIn(noticeIn, buttonIn) { + var myLength = arrays.length; + var q = 0; + var pauseTimer = null; + pauseTimer = Script.setInterval(function() { + q++; + qFade = q / 10.0; + Overlays.editOverlay(noticeIn, {alpha: qFade}); + Overlays.editOverlay(buttonIn, {alpha: qFade}); + if (q >= 9.0) { + Script.clearInterval(pauseTimer); + } + }, 10); +} + + +// push data from above to the 2 dimensional array +function createArrays(notice, button, createTime, height, myAlpha) { + arrays.push([notice, button, createTime, height, myAlpha]); +} +// handles mouse clicks on buttons +function mousePressEvent(event) { + var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); //identify which overlay was clicked + for (i = 0; i < buttons.length; i++) { //if user clicked a button + if(clickedOverlay == buttons[i]) { + Overlays.deleteOverlay(notifications[i]); + Overlays.deleteOverlay(buttons[i]); + notifications.splice(i, 1); + buttons.splice(i, 1); + times.splice(i, 1); + heights.splice(i, 1); + myAlpha.splice(i, 1); + arrays.splice(i, 1); + } + } +} + +// Control key remains active only while key is held down +function keyReleaseEvent(key) { + if (key.key == 16777249) { + ctrlIsPressed = false; + } +} + +// Triggers notification on specific key driven events +function keyPressEvent(key) { + if (key.key == 16777249) { + ctrlIsPressed = true; + } + if (key.text == "a") { + var noteString = "Turning to the Left"; + createNotification(noteString); + } + if (key.text == "d") { + var noteString = "Turning to the Right"; + createNotification(noteString); + } + if (key.text == "s") { + if (ctrlIsPressed == true){ + var noteString = "You have taken a snapshot"; + createNotification(noteString); + } + } + if (key.text == "q") { + var noteString = "Enable Scripted Motor control is now on."; + wordWrap(noteString); + } + if (key.text == "w") { + var noteString = "This notification spans 2 lines. The overlay will resize to fit new lines."; + var noteString = "editVoxels.js stopped, editModels.js stopped, selectAudioDevice.js stopped."; + wordWrap(noteString); + } + if (key.text == "e") { + var noteString = "This is an example of a multiple line notification. This notification will span 3 lines." + wordWrap(noteString); + } + if (key.text == "r") { + var noteString = "This is a very long line of text that we are going to use in this example to divide it into rows of maximum 43 chars and see how many lines we use."; + wordWrap(noteString); + } + if (key.text == "SPACE") { + var noteString = "You have pressed the Spacebar, This is an example of a multiple line notification. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam."; + wordWrap(noteString); + } +} + +// formats string to add newline every 43 chars +function wordWrap(str) { + var result = stringDivider(str, 43.0, "\n"); + createNotification(result); +} +// wraps whole word to newline +function stringDivider(str, slotWidth, spaceReplacer) { + if (str.length > slotWidth) { + var p = slotWidth; + for (; p > 0 && str[p] != ' '; p--) { + } + if (p > 0) { + var left = str.substring(0, p); + var right = str.substring(p + 1); + return left + spaceReplacer + stringDivider(right, slotWidth, spaceReplacer); + } + } + return str; +} + +// This fires a notification on window resize +function checkSize(){ + if((Window.innerWidth != ourWidth)||(Window.innerHeight != ourHeight)) { + var windowResize = "Window has been resized"; + ourWidth = Window.innerWidth; + ourHeight = Window.innerHeight; + windowDimensions = Controller.getViewportDimensions(); + overlayLocationX = (windowDimensions.x - (width + 60.0)); + buttonLocationX = overlayLocationX + (width - 35.0); + createNotification(windowResize) + } +} + +// Triggers notification if a user logs on or off +function onOnlineUsersChanged(users) { + var joiners = []; + var leavers = []; + for (user in users) { + if (last_users.indexOf(users[user]) == -1.0) { + joiners.push(users[user]); + createNotification(users[user] + " Has joined"); + } + } + for (user in last_users) { + if (users.indexOf(last_users[user]) == -1.0) { + leavers.push(last_users[user]); + createNotification(last_users[user] + " Has left"); + } + } + last_users = users; +} + +// Triggers notification if @MyUserName is mentioned in chat and returns the message to the notification. +function onIncomingMessage(user, message) { + var myMessage = message; + var alertMe = "@" + GlobalServices.myUsername; + var thisAlert = user + ": " + myMessage; + if (myMessage.indexOf(alertMe) > -1.0) { + wordWrap(thisAlert); + } +} +// Triggers mic mute notification +function onMuteStateChanged() { + var muteState = AudioDevice.getMuted() ? "Muted" : "Unmuted"; + var muteString = "Microphone is set to " + muteState; + createNotification(muteString); +} + +function update(){ + frame++; + if ((frame % 60.0) == 0) { // only update once a second + checkSize(); // checks for size change to trigger windowResize notification + locationY = 20.0; + for (var i = 0; i < arrays.length; i++) { //repositions overlays as others fade + var nextOverlay = Overlays.getOverlayAtPoint({x: overlayLocationX, y: locationY}); + Overlays.editOverlay(notifications[i], { x:overlayLocationX, y:locationY}); + Overlays.editOverlay(buttons[i], { x:buttonLocationX, y:locationY + 12.0}); + locationY = locationY + arrays[i][3]; + } + } + +// This checks the age of the notification and prepares to fade it after 9.0 seconds (var persistTime - 1) + for (var i = 0; i < arrays.length; i++) { + if (ready){ + var j = arrays[i][2]; + var k = j + persistTime; + if (k < (new Date().getTime() / 1000)) { + ready = false; + noticeOut = arrays[i][0]; + buttonOut = arrays[i][1]; + var arraysOut = i; + fadeOut(noticeOut, buttonOut, arraysOut); + } + } + } +} + +// this fades the notification ready for dismissal, and removes it from the arrays +function fadeOut(noticeOut, buttonOut, arraysOut) { + var myLength = arrays.length; + var r = 9.0; + var pauseTimer = null; + pauseTimer = Script.setInterval(function() { + r--; + rFade = r / 10.0; + Overlays.editOverlay(noticeOut, {alpha: rFade}); + Overlays.editOverlay(buttonOut, {alpha: rFade}); + if (r < 0) { + dismiss(noticeOut, buttonOut, arraysOut); + arrays.splice(arraysOut, 1); + ready = true; + Script.clearInterval(pauseTimer); + } + }, 20); +} + +// This handles the final dismissal of a notification after fading +function dismiss(firstNoteOut, firstButOut, firstOut) { + var working = firstOut + Overlays.deleteOverlay(firstNoteOut); + Overlays.deleteOverlay(firstButOut); + notifications.splice(firstOut, 1); + buttons.splice(firstOut, 1); + times.splice(firstOut, 1); + heights.splice(firstOut, 1); + myAlpha.splice(firstOut,1); +} + +onMuteStateChanged(); +AudioDevice.muteToggled.connect(onMuteStateChanged); +Controller.keyPressEvent.connect(keyPressEvent); +Controller.mousePressEvent.connect(mousePressEvent); +GlobalServices.onlineUsersChanged.connect(onOnlineUsersChanged); +GlobalServices.incomingMessage.connect(onIncomingMessage); +Controller.keyReleaseEvent.connect(keyReleaseEvent); +Script.update.connect(update); diff --git a/interface/resources/shaders/metavoxel_heightfield_base.vert b/interface/resources/shaders/metavoxel_heightfield_base.vert index 5486f5fa67..03a99bd57c 100644 --- a/interface/resources/shaders/metavoxel_heightfield_base.vert +++ b/interface/resources/shaders/metavoxel_heightfield_base.vert @@ -15,10 +15,10 @@ uniform sampler2D heightMap; // the distance between height points in texture space -uniform float heightScale; +uniform vec4 heightScale; // the scale between height and color textures -uniform float colorScale; +uniform vec2 colorScale; // the interpolated normal varying vec4 normal; @@ -26,14 +26,14 @@ varying vec4 normal; void main(void) { // transform and store the normal for interpolation vec2 heightCoord = gl_MultiTexCoord0.st; - vec4 neighborHeights = vec4(texture2D(heightMap, heightCoord - vec2(heightScale, 0.0)).r, - texture2D(heightMap, heightCoord + vec2(heightScale, 0.0)).r, - texture2D(heightMap, heightCoord - vec2(0.0, heightScale)).r, - texture2D(heightMap, heightCoord + vec2(0.0, heightScale)).r); - vec4 neighborsZero = step(1.0 / 255.0, neighborHeights); - normal = normalize(gl_ModelViewMatrix * vec4( - (neighborHeights.x - neighborHeights.y) * neighborsZero.x * neighborsZero.y, heightScale, - (neighborHeights.z - neighborHeights.w) * neighborsZero.z * neighborsZero.w, 0.0)); + vec4 neighborHeights = vec4(texture2D(heightMap, heightCoord - vec2(heightScale.s, 0.0)).r, + texture2D(heightMap, heightCoord + vec2(heightScale.s, 0.0)).r, + texture2D(heightMap, heightCoord - vec2(0.0, heightScale.t)).r, + texture2D(heightMap, heightCoord + vec2(0.0, heightScale.t)).r); + vec4 neighborsZero = step(1.0 / 65535.0, neighborHeights); + normal = vec4(normalize(gl_NormalMatrix * vec3( + heightScale.p * (neighborHeights.y - neighborHeights.x) * neighborsZero.x * neighborsZero.y, 1.0, + heightScale.q * (neighborHeights.w - neighborHeights.z) * neighborsZero.z * neighborsZero.w)), 0.0); // add the height to the position float height = texture2D(heightMap, heightCoord).r; @@ -43,5 +43,5 @@ void main(void) { gl_FrontColor = vec4(1.0, 1.0, 1.0, step(height, 0.0)); // pass along the scaled/offset texture coordinates - gl_TexCoord[0] = (gl_MultiTexCoord0 - vec4(heightScale, heightScale, 0.0, 0.0)) * colorScale; + gl_TexCoord[0] = vec4((heightCoord - heightScale.st) * colorScale, 0.0, 1.0); } diff --git a/interface/resources/shaders/metavoxel_heightfield_splat.vert b/interface/resources/shaders/metavoxel_heightfield_splat.vert index 926bcdd6c3..d54da347cc 100644 --- a/interface/resources/shaders/metavoxel_heightfield_splat.vert +++ b/interface/resources/shaders/metavoxel_heightfield_splat.vert @@ -18,10 +18,10 @@ uniform sampler2D heightMap; uniform sampler2D textureMap; // the distance between height points in texture space -uniform float heightScale; +uniform vec2 heightScale; // the scale between height and texture textures -uniform float textureScale; +uniform vec2 textureScale; // the splat texture offset uniform vec2 splatTextureOffset; @@ -58,7 +58,7 @@ void main(void) { gl_TexCoord[3] = textureSpacePosition * vec4(splatTextureScalesS[3], splatTextureScalesT[3], 0.0, 1.0); // compute the alpha values for each texture - float value = texture2D(textureMap, (gl_MultiTexCoord0.st - vec2(heightScale, heightScale)) * textureScale).r; + float value = texture2D(textureMap, (gl_MultiTexCoord0.st - heightScale) * textureScale).r; vec4 valueVector = vec4(value, value, value, value); alphaValues = step(textureValueMinima, valueVector) * step(valueVector, textureValueMaxima); } diff --git a/interface/resources/shaders/metavoxel_point.vert b/interface/resources/shaders/metavoxel_point.vert deleted file mode 100644 index 02de2bfa01..0000000000 --- a/interface/resources/shaders/metavoxel_point.vert +++ /dev/null @@ -1,31 +0,0 @@ -#version 120 - -// -// metavoxel_point.vert -// vertex shader -// -// Created by Andrzej Kapolka on 12/12/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -uniform float pointScale; - -// the interpolated normal -varying vec4 normal; - -void main(void) { - // transform and store the normal for interpolation - normal = vec4(normalize(gl_NormalMatrix * gl_Normal), 0.0); - - // extract the first three components of the vertex for position - gl_Position = gl_ModelViewProjectionMatrix * vec4(gl_Vertex.xyz, 1.0); - - // the final component is the size in world space - gl_PointSize = pointScale * gl_Vertex.w / gl_Position.w; - - // copy the color for interpolation - gl_FrontColor = vec4(gl_Color.rgb, 0.0); -} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c70e93168c..df64a4f80c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -608,7 +608,7 @@ void Application::initializeGL() { } // update before the first render - update(1.f / _fps); + update(1.0f / _fps); InfoView::showFirstTime(INFO_HELP_PATH); } @@ -672,7 +672,7 @@ void Application::paintGL() { // Update camera position if (!OculusManager::isConnected()) { - _myCamera.update(1.f / _fps); + _myCamera.update(1.0f / _fps); } // Note: whichCamera is used to pick between the normal camera myCamera for our @@ -697,7 +697,7 @@ void Application::paintGL() { _viewFrustumOffsetCamera.setRotation(_myCamera.getRotation() * frustumRotation); - _viewFrustumOffsetCamera.update(1.f/_fps); + _viewFrustumOffsetCamera.update(1.0f/_fps); whichCamera = &_viewFrustumOffsetCamera; } @@ -716,7 +716,7 @@ void Application::paintGL() { } else { OculusManager::display(_myAvatar->getWorldAlignedOrientation(), _myAvatar->getDefaultEyePosition(), *whichCamera); } - _myCamera.update(1.f / _fps); + _myCamera.update(1.0f / _fps); } else if (TV3DManager::isConnected()) { @@ -922,7 +922,7 @@ void Application::keyPressEvent(QKeyEvent* event) { if (!_myAvatar->getDriveKeys(UP)) { _myAvatar->jump(); } - _myAvatar->setDriveKeys(UP, 1.f); + _myAvatar->setDriveKeys(UP, 1.0f); break; case Qt::Key_Asterisk: @@ -931,14 +931,14 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_C: case Qt::Key_PageDown: - _myAvatar->setDriveKeys(DOWN, 1.f); + _myAvatar->setDriveKeys(DOWN, 1.0f); break; case Qt::Key_W: if (isOption && !isShifted && !isMeta) { Menu::getInstance()->triggerOption(MenuOption::Wireframe); } else { - _myAvatar->setDriveKeys(FWD, 1.f); + _myAvatar->setDriveKeys(FWD, 1.0f); } break; @@ -950,7 +950,7 @@ void Application::keyPressEvent(QKeyEvent* event) { } else if (!isOption && !isShifted && isMeta) { takeSnapshot(); } else { - _myAvatar->setDriveKeys(BACK, 1.f); + _myAvatar->setDriveKeys(BACK, 1.0f); } break; @@ -968,13 +968,13 @@ void Application::keyPressEvent(QKeyEvent* event) { if (isShifted) { Menu::getInstance()->triggerOption(MenuOption::Atmosphere); } else { - _myAvatar->setDriveKeys(ROT_LEFT, 1.f); + _myAvatar->setDriveKeys(ROT_LEFT, 1.0f); } break; case Qt::Key_D: if (!isMeta) { - _myAvatar->setDriveKeys(ROT_RIGHT, 1.f); + _myAvatar->setDriveKeys(ROT_RIGHT, 1.0f); } break; @@ -1002,7 +1002,7 @@ void Application::keyPressEvent(QKeyEvent* event) { _raiseMirror += 0.05f; } } else { - _myAvatar->setDriveKeys(isShifted ? UP : FWD, 1.f); + _myAvatar->setDriveKeys(isShifted ? UP : FWD, 1.0f); } break; @@ -1014,23 +1014,23 @@ void Application::keyPressEvent(QKeyEvent* event) { _raiseMirror -= 0.05f; } } else { - _myAvatar->setDriveKeys(isShifted ? DOWN : BACK, 1.f); + _myAvatar->setDriveKeys(isShifted ? DOWN : BACK, 1.0f); } break; case Qt::Key_Left: if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { - _rotateMirror += PI / 20.f; + _rotateMirror += PI / 20.0f; } else { - _myAvatar->setDriveKeys(isShifted ? LEFT : ROT_LEFT, 1.f); + _myAvatar->setDriveKeys(isShifted ? LEFT : ROT_LEFT, 1.0f); } break; case Qt::Key_Right: if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { - _rotateMirror -= PI / 20.f; + _rotateMirror -= PI / 20.0f; } else { - _myAvatar->setDriveKeys(isShifted ? RIGHT : ROT_RIGHT, 1.f); + _myAvatar->setDriveKeys(isShifted ? RIGHT : ROT_RIGHT, 1.0f); } break; @@ -1181,48 +1181,48 @@ void Application::keyReleaseEvent(QKeyEvent* event) { switch (event->key()) { case Qt::Key_E: case Qt::Key_PageUp: - _myAvatar->setDriveKeys(UP, 0.f); + _myAvatar->setDriveKeys(UP, 0.0f); break; case Qt::Key_C: case Qt::Key_PageDown: - _myAvatar->setDriveKeys(DOWN, 0.f); + _myAvatar->setDriveKeys(DOWN, 0.0f); break; case Qt::Key_W: - _myAvatar->setDriveKeys(FWD, 0.f); + _myAvatar->setDriveKeys(FWD, 0.0f); break; case Qt::Key_S: - _myAvatar->setDriveKeys(BACK, 0.f); + _myAvatar->setDriveKeys(BACK, 0.0f); break; case Qt::Key_A: - _myAvatar->setDriveKeys(ROT_LEFT, 0.f); + _myAvatar->setDriveKeys(ROT_LEFT, 0.0f); break; case Qt::Key_D: - _myAvatar->setDriveKeys(ROT_RIGHT, 0.f); + _myAvatar->setDriveKeys(ROT_RIGHT, 0.0f); break; case Qt::Key_Up: - _myAvatar->setDriveKeys(FWD, 0.f); - _myAvatar->setDriveKeys(UP, 0.f); + _myAvatar->setDriveKeys(FWD, 0.0f); + _myAvatar->setDriveKeys(UP, 0.0f); break; case Qt::Key_Down: - _myAvatar->setDriveKeys(BACK, 0.f); - _myAvatar->setDriveKeys(DOWN, 0.f); + _myAvatar->setDriveKeys(BACK, 0.0f); + _myAvatar->setDriveKeys(DOWN, 0.0f); break; case Qt::Key_Left: - _myAvatar->setDriveKeys(LEFT, 0.f); - _myAvatar->setDriveKeys(ROT_LEFT, 0.f); + _myAvatar->setDriveKeys(LEFT, 0.0f); + _myAvatar->setDriveKeys(ROT_LEFT, 0.0f); break; case Qt::Key_Right: - _myAvatar->setDriveKeys(RIGHT, 0.f); - _myAvatar->setDriveKeys(ROT_RIGHT, 0.f); + _myAvatar->setDriveKeys(RIGHT, 0.0f); + _myAvatar->setDriveKeys(ROT_RIGHT, 0.0f); break; case Qt::Key_Control: case Qt::Key_Shift: @@ -1526,7 +1526,7 @@ void Application::idle() { PerformanceTimer perfTimer("update"); PerformanceWarning warn(showWarnings, "Application::idle()... update()"); const float BIGGEST_DELTA_TIME_SECS = 0.25f; - update(glm::clamp((float)timeSinceLastUpdate / 1000.f, 0.f, BIGGEST_DELTA_TIME_SECS)); + update(glm::clamp((float)timeSinceLastUpdate / 1000.0f, 0.0f, BIGGEST_DELTA_TIME_SECS)); } { PerformanceTimer perfTimer("updateGL"); @@ -1657,8 +1657,8 @@ void Application::makeVoxel(glm::vec3 position, } glm::vec3 Application::getMouseVoxelWorldCoordinates(const VoxelDetail& mouseVoxel) { - return glm::vec3((mouseVoxel.x + mouseVoxel.s / 2.f) * TREE_SCALE, (mouseVoxel.y + mouseVoxel.s / 2.f) * TREE_SCALE, - (mouseVoxel.z + mouseVoxel.s / 2.f) * TREE_SCALE); + return glm::vec3((mouseVoxel.x + mouseVoxel.s / 2.0f) * TREE_SCALE, (mouseVoxel.y + mouseVoxel.s / 2.0f) * TREE_SCALE, + (mouseVoxel.z + mouseVoxel.s / 2.0f) * TREE_SCALE); } FaceTracker* Application::getActiveFaceTracker() { @@ -1871,7 +1871,7 @@ void Application::init() { 3.0f * TREE_SCALE / 2.0f)); _sharedVoxelSystemViewFrustum.setNearClip(TREE_SCALE / 2.0f); _sharedVoxelSystemViewFrustum.setFarClip(3.0f * TREE_SCALE / 2.0f); - _sharedVoxelSystemViewFrustum.setFieldOfView(90.f); + _sharedVoxelSystemViewFrustum.setFieldOfView(90.0f); _sharedVoxelSystemViewFrustum.setOrientation(glm::quat()); _sharedVoxelSystemViewFrustum.calculate(); _sharedVoxelSystem.setViewFrustum(&_sharedVoxelSystemViewFrustum); @@ -2172,7 +2172,7 @@ void Application::updateMyAvatarLookAtPosition() { } else { // I am not looking at anyone else, so just look forward lookAtSpot = _myAvatar->getHead()->getEyePosition() + - (_myAvatar->getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.f, 0.f, -TREE_SCALE)); + (_myAvatar->getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.0f, 0.0f, -TREE_SCALE)); } } // @@ -2333,7 +2333,7 @@ void Application::update(float deltaTime) { } #endif // USE_BULLET_PHYSICS - { + if (!_aboutToQuit) { PerformanceTimer perfTimer("entities"); _entities.update(); // update the models... { @@ -3225,12 +3225,12 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { if (billboard) { _mirrorCamera.setFieldOfView(BILLBOARD_FIELD_OF_VIEW); // degees _mirrorCamera.setPosition(_myAvatar->getPosition() + - _myAvatar->getOrientation() * glm::vec3(0.f, 0.f, -1.0f) * BILLBOARD_DISTANCE * _myAvatar->getScale()); + _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * BILLBOARD_DISTANCE * _myAvatar->getScale()); } else if (_rearMirrorTools->getZoomLevel() == BODY) { _mirrorCamera.setFieldOfView(MIRROR_FIELD_OF_VIEW); // degrees _mirrorCamera.setPosition(_myAvatar->getChestPosition() + - _myAvatar->getOrientation() * glm::vec3(0.f, 0.f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale()); + _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale()); } else { // HEAD zoom level _mirrorCamera.setFieldOfView(MIRROR_FIELD_OF_VIEW); // degrees @@ -3238,11 +3238,11 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { // as a hack until we have a better way of dealing with coordinate precision issues, reposition the // face/body so that the average eye position lies at the origin eyeRelativeCamera = true; - _mirrorCamera.setPosition(_myAvatar->getOrientation() * glm::vec3(0.f, 0.f, -1.0f) * MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale()); + _mirrorCamera.setPosition(_myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale()); } else { _mirrorCamera.setPosition(_myAvatar->getHead()->getEyePosition() + - _myAvatar->getOrientation() * glm::vec3(0.f, 0.f, -1.0f) * MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale()); + _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale()); } } _mirrorCamera.setAspectRatio((float)region.width() / region.height()); @@ -4258,7 +4258,7 @@ void Application::toggleLogDialog() { } void Application::initAvatarAndViewFrustum() { - updateMyAvatar(0.f); + updateMyAvatar(0.0f); } void Application::checkVersion() { @@ -4432,7 +4432,7 @@ unsigned int Application::getRenderTargetFramerate() const { float Application::getRenderResolutionScale() const { if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionOne)) { - return 1.f; + return 1.0f; } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionTwoThird)) { return 0.666f; } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionHalf)) { @@ -4442,6 +4442,6 @@ float Application::getRenderResolutionScale() const { } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionQuarter)) { return 0.25f; } else { - return 1.f; + return 1.0f; } } diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index b7076b6c1a..75b8c252f7 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -115,7 +115,6 @@ Audio::Audio(QObject* parent) : _samplesPerScope(NETWORK_SAMPLES_PER_FRAME * _framesPerScope), _noiseSourceEnabled(false), _toneSourceEnabled(true), - _peqEnabled(false), _scopeInput(0), _scopeOutputLeft(0), _scopeOutputRight(0), @@ -153,7 +152,6 @@ void Audio::init(QGLWidget *parent) { void Audio::reset() { _receivedAudioStream.reset(); resetStats(); - _peq.reset(); _noiseSource.reset(); _toneSource.reset(); _sourceGain.reset(); @@ -457,7 +455,6 @@ void Audio::start() { } _inputFrameBuffer.initialize( _inputFormat.channelCount(), _audioInput->bufferSize() * 8 ); - _peq.initialize( _inputFormat.sampleRate() ); _inputGain.initialize(); _sourceGain.initialize(); _noiseSource.initialize(); @@ -469,7 +466,6 @@ void Audio::start() { void Audio::stop() { _inputFrameBuffer.finalize(); - _peq.finalize(); _inputGain.finalize(); _sourceGain.finalize(); _noiseSource.finalize(); @@ -664,7 +660,7 @@ void Audio::handleAudioInput() { QByteArray inputByteArray = _inputDevice->readAll(); - if (!_muted && (_audioSourceInjectEnabled || _peqEnabled)) { + if (!_muted && _audioSourceInjectEnabled) { int16_t* inputFrameData = (int16_t*)inputByteArray.data(); const uint32_t inputFrameCount = inputByteArray.size() / sizeof(int16_t); @@ -685,10 +681,6 @@ void Audio::handleAudioInput() { } _sourceGain.render(_inputFrameBuffer); // post gain } - if (_peqEnabled) { - _peq.render(_inputFrameBuffer); // 3-band parametric eq - } - _inputFrameBuffer.copyFrames(1, inputFrameCount, inputFrameData, true /*copy out*/); } @@ -1378,10 +1370,14 @@ bool Audio::outputLocalInjector(bool isStereo, qreal volume, AudioInjector* inje localFormat, this); localOutput->setVolume(volume); - // add this to our list of local injected outputs, we will need to clean it up when the injector says it is done - _injectedOutputInterfaces.insert(injector, localOutput); + // move the localOutput to the same thread as the local injector buffer + localOutput->moveToThread(injector->getLocalBuffer()->thread()); - connect(injector, &AudioInjector::finished, this, &Audio::cleanupLocalOutputInterface); + // have it be cleaned up when that thread is done + connect(injector->thread(), &QThread::finished, localOutput, &QAudioOutput::stop); + connect(injector->thread(), &QThread::finished, localOutput, &QAudioOutput::deleteLater); + + qDebug() << "Starting QAudioOutput for local injector" << localOutput; localOutput->start(injector->getLocalBuffer()); return localOutput->state() == QAudio::ActiveState; @@ -1390,16 +1386,6 @@ bool Audio::outputLocalInjector(bool isStereo, qreal volume, AudioInjector* inje return false; } -void Audio::cleanupLocalOutputInterface() { - QAudioOutput* outputInterface = _injectedOutputInterfaces.value(sender()); - if (outputInterface) { - qDebug() << "Stopping a QAudioOutput interface since injector" << sender() << "is finished"; - - outputInterface->stop(); - outputInterface->deleteLater(); - } -} - void Audio::renderToolBox(int x, int y, bool boxed) { glEnable(GL_TEXTURE_2D); @@ -1479,31 +1465,6 @@ void Audio::renderToolBox(int x, int y, bool boxed) { glDisable(GL_TEXTURE_2D); } -void Audio::toggleAudioFilter() { - _peqEnabled = !_peqEnabled; -} - -void Audio::selectAudioFilterFlat() { - if (Menu::getInstance()->isOptionChecked(MenuOption::AudioFilterFlat)) { - _peq.loadProfile(0); - } -} -void Audio::selectAudioFilterTrebleCut() { - if (Menu::getInstance()->isOptionChecked(MenuOption::AudioFilterTrebleCut)) { - _peq.loadProfile(1); - } -} -void Audio::selectAudioFilterBassCut() { - if (Menu::getInstance()->isOptionChecked(MenuOption::AudioFilterBassCut)) { - _peq.loadProfile(2); - } -} -void Audio::selectAudioFilterSmiley() { - if (Menu::getInstance()->isOptionChecked(MenuOption::AudioFilterSmiley)) { - _peq.loadProfile(3); - } -} - void Audio::toggleScope() { _scopeEnabled = !_scopeEnabled; if (_scopeEnabled) { diff --git a/interface/src/Audio.h b/interface/src/Audio.h index 127c2e3332..166042878c 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -26,8 +26,6 @@ #include "AudioSourceTone.h" #include "AudioSourceNoise.h" #include "AudioGain.h" -#include "AudioFilter.h" -#include "AudioFilterBank.h" #include #include @@ -47,10 +45,17 @@ #include #include +#ifdef _WIN32 +#pragma warning( push ) +#pragma warning( disable : 4305 ) +#endif extern "C" { #include #include } +#ifdef _WIN32 +#pragma warning( pop ) +#endif static const int NUM_AUDIO_CHANNELS = 2; @@ -81,7 +86,7 @@ public: // setup for audio I/O Audio(QObject* parent = 0); - float getLastInputLoudness() const { return glm::max(_lastInputLoudness - _noiseGateMeasuredFloor, 0.f); } + float getLastInputLoudness() const { return glm::max(_lastInputLoudness - _noiseGateMeasuredFloor, 0.0f); } float getTimeSinceLastClip() const { return _timeSinceLastClip; } float getAudioAverageInputLoudness() const { return _lastInputLoudness; } @@ -149,11 +154,6 @@ public slots: void addLastFrameRepeatedWithFadeToScope(int samplesPerChannel); void addStereoSamplesToScope(const QByteArray& samples); void processReceivedSamples(const QByteArray& inputBuffer, QByteArray& outputBuffer); - void toggleAudioFilter(); - void selectAudioFilterFlat(); - void selectAudioFilterTrebleCut(); - void selectAudioFilterBassCut(); - void selectAudioFilterSmiley(); virtual bool outputLocalInjector(bool isStereo, qreal volume, AudioInjector* injector); @@ -179,9 +179,6 @@ signals: void preProcessOriginalInboundAudio(unsigned int sampleTime, QByteArray& samples, const QAudioFormat& format); void processInboundAudio(unsigned int sampleTime, const QByteArray& samples, const QAudioFormat& format); void processLocalAudio(unsigned int sampleTime, const QByteArray& samples, const QAudioFormat& format); - -private slots: - void cleanupLocalOutputInterface(); private: void outputFormatChanged(); @@ -335,9 +332,6 @@ private: bool _toneSourceEnabled; AudioSourceTone _toneSource; - // Multi-band parametric EQ - bool _peqEnabled; - AudioFilterPEQ3m _peq; QMutex _guard; QByteArray* _scopeInput; @@ -369,8 +363,6 @@ private: AudioOutputIODevice _audioOutputIODevice; WeakRecorderPointer _recorder; - - QHash _injectedOutputInterfaces; }; diff --git a/interface/src/Camera.cpp b/interface/src/Camera.cpp index 121833bd16..6e5a83790e 100644 --- a/interface/src/Camera.cpp +++ b/interface/src/Camera.cpp @@ -108,6 +108,12 @@ PickRay Camera::computePickRay(float x, float y) { return result; } +PickRay Camera::computeViewPickRay(float xRatio, float yRatio) { + PickRay result; + Application::getInstance()->getViewFrustum()->computePickRay(xRatio, yRatio, result.origin, result.direction); + return result; +} + void Camera::setModeString(const QString& mode) { CameraMode targetMode = stringToMode(mode); diff --git a/interface/src/Camera.h b/interface/src/Camera.h index 06db3dc3ef..7fcf9404cd 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -79,6 +79,7 @@ public slots: glm::quat getOrientation() const { return getRotation(); } PickRay computePickRay(float x, float y); + PickRay computeViewPickRay(float xRatio, float yRatio); signals: void modeUpdated(const QString& newMode); diff --git a/interface/src/Environment.cpp b/interface/src/Environment.cpp index 631e3da1cf..0ce43ecdaa 100644 --- a/interface/src/Environment.cpp +++ b/interface/src/Environment.cpp @@ -80,14 +80,14 @@ void Environment::renderAtmospheres(Camera& camera) { glm::vec3 Environment::getGravity (const glm::vec3& position) { // // 'Default' gravity pulls you downward in Y when you are near the X/Z plane - const glm::vec3 DEFAULT_GRAVITY(0.f, -1.f, 0.f); + const glm::vec3 DEFAULT_GRAVITY(0.0f, -1.0f, 0.0f); glm::vec3 gravity(DEFAULT_GRAVITY); - float DEFAULT_SURFACE_RADIUS = 30.f; + float DEFAULT_SURFACE_RADIUS = 30.0f; float gravityStrength; // Weaken gravity with height - if (position.y > 0.f) { - gravityStrength = 1.f / powf((DEFAULT_SURFACE_RADIUS + position.y) / DEFAULT_SURFACE_RADIUS, 2.f); + if (position.y > 0.0f) { + gravityStrength = 1.0f / powf((DEFAULT_SURFACE_RADIUS + position.y) / DEFAULT_SURFACE_RADIUS, 2.0f); gravity *= gravityStrength; } @@ -103,7 +103,7 @@ glm::vec3 Environment::getGravity (const glm::vec3& position) { gravity += glm::normalize(vector) * environmentData.getGravity(); } else { // Outside a planet, the gravity falls off with distance - gravityStrength = 1.f / powf(glm::length(vector) / surfaceRadius, 2.f); + gravityStrength = 1.0f / powf(glm::length(vector) / surfaceRadius, 2.0f); gravity += glm::normalize(vector) * environmentData.getGravity() * gravityStrength; } } @@ -261,7 +261,7 @@ void Environment::renderAtmosphere(Camera& camera, const EnvironmentData& data) glDepthMask(GL_FALSE); glDisable(GL_DEPTH_TEST); - Application::getInstance()->getGeometryCache()->renderSphere(1.f, 100, 50); //Draw a unit sphere + Application::getInstance()->getGeometryCache()->renderSphere(1.0f, 100, 50); //Draw a unit sphere glDepthMask(GL_TRUE); program->release(); diff --git a/interface/src/Hair.cpp b/interface/src/Hair.cpp index 119f5a31d0..e14fc519d3 100644 --- a/interface/src/Hair.cpp +++ b/interface/src/Hair.cpp @@ -78,7 +78,7 @@ Hair::Hair(int strands, } _hairOriginalPosition[vertexIndex] = _hairLastPosition[vertexIndex] = _hairPosition[vertexIndex] = thisVertex; - _hairQuadDelta[vertexIndex] = glm::vec3(cos(strandAngle) * _hairThickness, 0.f, sin(strandAngle) * _hairThickness); + _hairQuadDelta[vertexIndex] = glm::vec3(cos(strandAngle) * _hairThickness, 0.0f, sin(strandAngle) * _hairThickness); _hairQuadDelta[vertexIndex] *= ((float)link / _links); _hairNormals[vertexIndex] = glm::normalize(randVector()); if (randFloat() < elevation / PI_OVER_TWO) { @@ -155,7 +155,7 @@ void Hair::simulate(float deltaTime) { // Add stiffness to return to original position _hairPosition[vertexIndex] += (_hairOriginalPosition[vertexIndex] - _hairPosition[vertexIndex]) - * powf(1.f - (float)link / _links, 2.f) * HAIR_STIFFNESS; + * powf(1.0f - (float)link / _links, 2.0f) * HAIR_STIFFNESS; // Add angular acceleration const float ANGULAR_VELOCITY_MIN = 0.001f; @@ -163,30 +163,30 @@ void Hair::simulate(float deltaTime) { glm::vec3 yawVector = _hairPosition[vertexIndex]; glm::vec3 angularVelocity = _angularVelocity * HAIR_ANGULAR_VELOCITY_COUPLING; glm::vec3 angularAcceleration = _angularAcceleration * HAIR_ANGULAR_ACCELERATION_COUPLING; - yawVector.y = 0.f; + yawVector.y = 0.0f; if (glm::length(yawVector) > EPSILON) { float radius = glm::length(yawVector); yawVector = glm::normalize(yawVector); float angle = atan2f(yawVector.x, -yawVector.z) + PI; - glm::vec3 delta = glm::vec3(-1.f, 0.f, 0.f) * glm::angleAxis(angle, glm::vec3(0, 1, 0)); + glm::vec3 delta = glm::vec3(-1.0f, 0.0f, 0.0f) * glm::angleAxis(angle, glm::vec3(0, 1, 0)); _hairPosition[vertexIndex] -= delta * radius * (angularVelocity.y - angularAcceleration.y) * deltaTime; } glm::vec3 pitchVector = _hairPosition[vertexIndex]; - pitchVector.x = 0.f; + pitchVector.x = 0.0f; if (glm::length(pitchVector) > EPSILON) { float radius = glm::length(pitchVector); pitchVector = glm::normalize(pitchVector); float angle = atan2f(pitchVector.y, -pitchVector.z) + PI; - glm::vec3 delta = glm::vec3(0.0f, 1.0f, 0.f) * glm::angleAxis(angle, glm::vec3(1, 0, 0)); + glm::vec3 delta = glm::vec3(0.0f, 1.0f, 0.0f) * glm::angleAxis(angle, glm::vec3(1, 0, 0)); _hairPosition[vertexIndex] -= delta * radius * (angularVelocity.x - angularAcceleration.x) * deltaTime; } glm::vec3 rollVector = _hairPosition[vertexIndex]; - rollVector.z = 0.f; + rollVector.z = 0.0f; if (glm::length(rollVector) > EPSILON) { float radius = glm::length(rollVector); pitchVector = glm::normalize(rollVector); float angle = atan2f(rollVector.x, rollVector.y) + PI; - glm::vec3 delta = glm::vec3(-1.0f, 0.0f, 0.f) * glm::angleAxis(angle, glm::vec3(0, 0, 1)); + glm::vec3 delta = glm::vec3(-1.0f, 0.0f, 0.0f) * glm::angleAxis(angle, glm::vec3(0, 0, 1)); _hairPosition[vertexIndex] -= delta * radius * (angularVelocity.z - angularAcceleration.z) * deltaTime; } } @@ -216,16 +216,16 @@ void Hair::render() { const float HAIR_SETBACK = 0.0f; int sparkleIndex = (int) (randFloat() * SPARKLE_EVERY); glPushMatrix(); - glTranslatef(0.f, 0.f, HAIR_SETBACK); + glTranslatef(0.0f, 0.0f, HAIR_SETBACK); glBegin(GL_QUADS); for (int strand = 0; strand < _strands; strand++) { for (int link = 0; link < _links - 1; link++) { int vertexIndex = strand * _links + link; glm::vec3 thisColor = _hairColors[vertexIndex]; if (sparkleIndex % SPARKLE_EVERY == 0) { - thisColor.x += (1.f - thisColor.x) * loudnessFactor; - thisColor.y += (1.f - thisColor.y) * loudnessFactor; - thisColor.z += (1.f - thisColor.z) * loudnessFactor; + thisColor.x += (1.0f - thisColor.x) * loudnessFactor; + thisColor.y += (1.0f - thisColor.y) * loudnessFactor; + thisColor.z += (1.0f - thisColor.z) * loudnessFactor; } glColor3fv(&thisColor.x); glNormal3fv(&_hairNormals[vertexIndex].x); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 1eacfa8363..db5cd5170d 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -446,7 +446,7 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(entitiesDebugMenu, MenuOption::DisableLightEntities, 0, false); addCheckableActionToQMenuAndActionHash(entitiesDebugMenu, MenuOption::DontReduceMaterialSwitches, 0, false); - addCheckableActionToQMenuAndActionHash(entitiesDebugMenu, MenuOption::RenderEntitiesAsScene, 0, false); + addCheckableActionToQMenuAndActionHash(entitiesDebugMenu, MenuOption::DontRenderEntitiesAsScene, 0, false); QMenu* entityCullingMenu = entitiesDebugMenu->addMenu("Culling"); addCheckableActionToQMenuAndActionHash(entityCullingMenu, MenuOption::DontCullOutOfViewMeshParts, 0, false); @@ -464,7 +464,7 @@ Menu::Menu() : QMenu* metavoxelOptionsMenu = developerMenu->addMenu("Metavoxels"); addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::DisplayHermiteData, 0, false, Application::getInstance()->getMetavoxels(), SLOT(refreshVoxelData())); - addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::RenderHeightfields, 0, true); + addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::RenderSpanners, 0, true); addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::RenderDualContourSurfaces, 0, true); addActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::NetworkSimulator, 0, this, SLOT(showMetavoxelNetworkSimulator())); @@ -545,47 +545,6 @@ Menu::Menu() : appInstance->getAudio(), SLOT(toggleAudioNoiseReduction())); - addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioFilter, - 0, - false, - appInstance->getAudio(), - SLOT(toggleAudioFilter())); - - QMenu* audioFilterMenu = audioDebugMenu->addMenu("Audio Filter"); - addDisabledActionAndSeparator(audioFilterMenu, "Filter Response"); - { - QAction *flat = addCheckableActionToQMenuAndActionHash(audioFilterMenu, MenuOption::AudioFilterFlat, - 0, - true, - appInstance->getAudio(), - SLOT(selectAudioFilterFlat())); - - QAction *trebleCut = addCheckableActionToQMenuAndActionHash(audioFilterMenu, MenuOption::AudioFilterTrebleCut, - 0, - false, - appInstance->getAudio(), - SLOT(selectAudioFilterTrebleCut())); - - QAction *bassCut = addCheckableActionToQMenuAndActionHash(audioFilterMenu, MenuOption::AudioFilterBassCut, - 0, - false, - appInstance->getAudio(), - SLOT(selectAudioFilterBassCut())); - - QAction *smiley = addCheckableActionToQMenuAndActionHash(audioFilterMenu, MenuOption::AudioFilterSmiley, - 0, - false, - appInstance->getAudio(), - SLOT(selectAudioFilterSmiley())); - - - QActionGroup* audioFilterGroup = new QActionGroup(audioFilterMenu); - audioFilterGroup->addAction(flat); - audioFilterGroup->addAction(trebleCut); - audioFilterGroup->addAction(bassCut); - audioFilterGroup->addAction(smiley); - } - addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio); addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoLocalAudio); addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::StereoAudio, 0, false, @@ -625,17 +584,16 @@ Menu::Menu() : audioSourceGroup->addAction(sine440); } - addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScope, + QMenu* audioScopeMenu = audioDebugMenu->addMenu("Audio Scope"); + addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScope, Qt::CTRL | Qt::Key_P, false, appInstance->getAudio(), SLOT(toggleScope())); - addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScopePause, + addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopePause, Qt::CTRL | Qt::SHIFT | Qt::Key_P , false, appInstance->getAudio(), SLOT(toggleScopePause())); - - QMenu* audioScopeMenu = audioDebugMenu->addMenu("Audio Scope"); addDisabledActionAndSeparator(audioScopeMenu, "Display Frames"); { QAction *fiveFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeFiveFrames, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 7ed8811b4f..7e153eba5e 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -332,17 +332,12 @@ namespace MenuOption { const QString Animations = "Animations..."; const QString Atmosphere = "Atmosphere"; const QString Attachments = "Attachments..."; - const QString AudioFilter = "Audio Filter Bank"; - const QString AudioFilterFlat = "Flat Response"; - const QString AudioFilterTrebleCut= "Treble Cut"; - const QString AudioFilterBassCut = "Bass Cut"; - const QString AudioFilterSmiley = "Smiley Curve"; const QString AudioNoiseReduction = "Audio Noise Reduction"; - const QString AudioScope = "Audio Scope"; + const QString AudioScope = "Show Scope"; const QString AudioScopeFiftyFrames = "Fifty"; const QString AudioScopeFiveFrames = "Five"; const QString AudioScopeFrames = "Display Frames"; - const QString AudioScopePause = "Pause Audio Scope"; + const QString AudioScopePause = "Pause Scope"; const QString AudioScopeTwentyFrames = "Twenty"; const QString AudioStats = "Audio Stats"; const QString AudioStatsShowInjectedStreams = "Audio Stats Show Injected Streams"; @@ -378,6 +373,7 @@ namespace MenuOption { const QString DontCullOutOfViewMeshParts = "Don't Cull Out Of View Mesh Parts"; const QString DontCullTooSmallMeshParts = "Don't Cull Too Small Mesh Parts"; const QString DontReduceMaterialSwitches = "Don't Attempt to Reduce Material Switches"; + const QString DontRenderEntitiesAsScene = "Don't Render Entities as Scene"; const QString DecreaseAvatarSize = "Decrease Avatar Size"; const QString DecreaseVoxelSize = "Decrease Voxel Size"; const QString DisableActivityLogger = "Disable Activity Logger"; @@ -448,12 +444,11 @@ namespace MenuOption { const QString ReloadAllScripts = "Reload All Scripts"; const QString RenderBoundingCollisionShapes = "Show Bounding Collision Shapes"; const QString RenderDualContourSurfaces = "Render Dual Contour Surfaces"; - const QString RenderEntitiesAsScene = "Render Entities as Scene"; const QString RenderFocusIndicator = "Show Eye Focus"; const QString RenderHeadCollisionShapes = "Show Head Collision Shapes"; - const QString RenderHeightfields = "Render Heightfields"; const QString RenderLookAtVectors = "Show Look-at Vectors"; const QString RenderSkeletonCollisionShapes = "Show Skeleton Collision Shapes"; + const QString RenderSpanners = "Render Spanners"; const QString RenderTargetFramerate = "Framerate"; const QString RenderTargetFramerateUnlimited = "Unlimited"; const QString RenderTargetFramerate60 = "60"; diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 5d8f77eedc..b98fea8eca 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -36,8 +36,7 @@ REGISTER_META_OBJECT(DefaultMetavoxelRendererImplementation) REGISTER_META_OBJECT(SphereRenderer) REGISTER_META_OBJECT(CuboidRenderer) REGISTER_META_OBJECT(StaticModelRenderer) - -static int bufferPointVectorMetaTypeId = qRegisterMetaType(); +REGISTER_META_OBJECT(HeightfieldRenderer) MetavoxelSystem::NetworkSimulation::NetworkSimulation(float dropRate, float repeatRate, int minimumDelay, int maximumDelay, int bandwidthLimit) : @@ -57,19 +56,50 @@ MetavoxelSystem::~MetavoxelSystem() { void MetavoxelSystem::init() { MetavoxelClientManager::init(); - DefaultMetavoxelRendererImplementation::init(); - - _pointBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(new BufferDataAttribute("pointBuffer")); - - _heightfieldBufferAttribute = AttributeRegistry::getInstance()->registerAttribute( - new BufferDataAttribute("heightfieldBuffer")); - _heightfieldBufferAttribute->setLODThresholdMultiplier( - AttributeRegistry::getInstance()->getHeightfieldAttribute()->getLODThresholdMultiplier()); _voxelBufferAttribute = AttributeRegistry::getInstance()->registerAttribute( new BufferDataAttribute("voxelBuffer")); _voxelBufferAttribute->setLODThresholdMultiplier( AttributeRegistry::getInstance()->getVoxelColorAttribute()->getLODThresholdMultiplier()); + + _baseHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + + "shaders/metavoxel_heightfield_base.vert"); + _baseHeightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + + "shaders/metavoxel_heightfield_base.frag"); + _baseHeightfieldProgram.link(); + + _baseHeightfieldProgram.bind(); + _baseHeightfieldProgram.setUniformValue("heightMap", 0); + _baseHeightfieldProgram.setUniformValue("diffuseMap", 1); + _baseHeightScaleLocation = _baseHeightfieldProgram.uniformLocation("heightScale"); + _baseColorScaleLocation = _baseHeightfieldProgram.uniformLocation("colorScale"); + _baseHeightfieldProgram.release(); + + loadSplatProgram("heightfield", _splatHeightfieldProgram, _splatHeightfieldLocations); + + _heightfieldCursorProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + + "shaders/metavoxel_heightfield_cursor.vert"); + _heightfieldCursorProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + + "shaders/metavoxel_cursor.frag"); + _heightfieldCursorProgram.link(); + + _heightfieldCursorProgram.bind(); + _heightfieldCursorProgram.setUniformValue("heightMap", 0); + _heightfieldCursorProgram.release(); + + _baseVoxelProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + + "shaders/metavoxel_voxel_base.vert"); + _baseVoxelProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + + "shaders/metavoxel_voxel_base.frag"); + _baseVoxelProgram.link(); + + loadSplatProgram("voxel", _splatVoxelProgram, _splatVoxelLocations); + + _voxelCursorProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + + "shaders/metavoxel_voxel_cursor.vert"); + _voxelCursorProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + + "shaders/metavoxel_cursor.frag"); + _voxelCursorProgram.link(); } MetavoxelLOD MetavoxelSystem::getLOD() { @@ -148,6 +178,18 @@ int RenderVisitor::visit(MetavoxelInfo& info) { return STOP_RECURSION; } +class HeightfieldPoint { +public: + glm::vec3 vertex; + glm::vec2 textureCoord; +}; + +const int SPLAT_COUNT = 4; +const GLint SPLAT_TEXTURE_UNITS[] = { 3, 4, 5, 6 }; + +static const int EIGHT_BIT_MAXIMUM = 255; +static const float EIGHT_BIT_MAXIMUM_RECIPROCAL = 1.0f / EIGHT_BIT_MAXIMUM; + void MetavoxelSystem::render() { // update the frustum ViewFrustum* viewFrustum = Application::getInstance()->getDisplayViewFrustum(); @@ -158,6 +200,267 @@ void MetavoxelSystem::render() { RenderVisitor renderVisitor(getLOD()); guideToAugmented(renderVisitor, true); + if (!_heightfieldBaseBatches.isEmpty()) { + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, true); + + glDisable(GL_BLEND); + glEnable(GL_CULL_FACE); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_EQUAL, 0.0f); + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + _baseHeightfieldProgram.bind(); + + foreach (const HeightfieldBaseLayerBatch& batch, _heightfieldBaseBatches) { + glPushMatrix(); + glTranslatef(batch.translation.x, batch.translation.y, batch.translation.z); + glm::vec3 axis = glm::axis(batch.rotation); + glRotatef(glm::degrees(glm::angle(batch.rotation)), axis.x, axis.y, axis.z); + glScalef(batch.scale.x, batch.scale.y, batch.scale.z); + + batch.vertexBuffer->bind(); + batch.indexBuffer->bind(); + + HeightfieldPoint* point = 0; + glVertexPointer(3, GL_FLOAT, sizeof(HeightfieldPoint), &point->vertex); + glTexCoordPointer(2, GL_FLOAT, sizeof(HeightfieldPoint), &point->textureCoord); + + glBindTexture(GL_TEXTURE_2D, batch.heightTextureID); + + _baseHeightfieldProgram.setUniform(_baseHeightScaleLocation, batch.heightScale); + _baseHeightfieldProgram.setUniform(_baseColorScaleLocation, batch.colorScale); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, batch.colorTextureID); + + glDrawRangeElements(GL_TRIANGLES, 0, batch.vertexCount - 1, batch.indexCount, GL_UNSIGNED_INT, 0); + + glBindTexture(GL_TEXTURE_2D, 0); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + + batch.vertexBuffer->release(); + batch.indexBuffer->release(); + + glPopMatrix(); + } + + Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false); + + _baseHeightfieldProgram.release(); + + glDisable(GL_ALPHA_TEST); + glEnable(GL_BLEND); + + if (!_heightfieldSplatBatches.isEmpty()) { + glDepthFunc(GL_LEQUAL); + glDepthMask(false); + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(-1.0f, -1.0f); + + _splatHeightfieldProgram.bind(); + + foreach (const HeightfieldSplatBatch& batch, _heightfieldSplatBatches) { + glPushMatrix(); + glTranslatef(batch.translation.x, batch.translation.y, batch.translation.z); + glm::vec3 axis = glm::axis(batch.rotation); + glRotatef(glm::degrees(glm::angle(batch.rotation)), axis.x, axis.y, axis.z); + glScalef(batch.scale.x, batch.scale.y, batch.scale.z); + + batch.vertexBuffer->bind(); + batch.indexBuffer->bind(); + + HeightfieldPoint* point = 0; + glVertexPointer(3, GL_FLOAT, sizeof(HeightfieldPoint), &point->vertex); + glTexCoordPointer(2, GL_FLOAT, sizeof(HeightfieldPoint), &point->textureCoord); + + glBindTexture(GL_TEXTURE_2D, batch.heightTextureID); + + _splatHeightfieldProgram.setUniformValue(_splatHeightfieldLocations.heightScale, + batch.heightScale.x, batch.heightScale.y); + _splatHeightfieldProgram.setUniform(_splatHeightfieldLocations.textureScale, batch.textureScale); + _splatHeightfieldProgram.setUniform(_splatHeightfieldLocations.splatTextureOffset, batch.splatTextureOffset); + + const float QUARTER_STEP = 0.25f * EIGHT_BIT_MAXIMUM_RECIPROCAL; + _splatHeightfieldProgram.setUniform(_splatHeightfieldLocations.splatTextureScalesS, batch.splatTextureScalesS); + _splatHeightfieldProgram.setUniform(_splatHeightfieldLocations.splatTextureScalesT, batch.splatTextureScalesT); + _splatHeightfieldProgram.setUniformValue( + _splatHeightfieldLocations.textureValueMinima, + (batch.materialIndex + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, + (batch.materialIndex + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, + (batch.materialIndex + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, + (batch.materialIndex + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP); + _splatHeightfieldProgram.setUniformValue( + _splatHeightfieldLocations.textureValueMaxima, + (batch.materialIndex + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, + (batch.materialIndex + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, + (batch.materialIndex + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, + (batch.materialIndex + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, batch.materialTextureID); + + for (int i = 0; i < SPLAT_COUNT; i++) { + glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[i]); + glBindTexture(GL_TEXTURE_2D, batch.splatTextureIDs[i]); + } + + glDrawRangeElements(GL_TRIANGLES, 0, batch.vertexCount - 1, batch.indexCount, GL_UNSIGNED_INT, 0); + + for (int i = 0; i < SPLAT_COUNT; i++) { + glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[i]); + glBindTexture(GL_TEXTURE_2D, 0); + } + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, 0); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + + batch.vertexBuffer->release(); + batch.indexBuffer->release(); + + glPopMatrix(); + } + + _splatHeightfieldProgram.release(); + + glDisable(GL_POLYGON_OFFSET_FILL); + glDepthMask(true); + glDepthFunc(GL_LESS); + + _heightfieldSplatBatches.clear(); + } + + glDisable(GL_CULL_FACE); + + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + _heightfieldBaseBatches.clear(); + } + + if (!_voxelBaseBatches.isEmpty()) { + Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, true); + + glEnableClientState(GL_VERTEX_ARRAY); + glDisable(GL_BLEND); + glEnable(GL_CULL_FACE); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_EQUAL, 0.0f); + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_NORMAL_ARRAY); + + _baseVoxelProgram.bind(); + + foreach (const VoxelBatch& batch, _voxelBaseBatches) { + batch.vertexBuffer->bind(); + batch.indexBuffer->bind(); + + VoxelPoint* point = 0; + glVertexPointer(3, GL_FLOAT, sizeof(VoxelPoint), &point->vertex); + glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VoxelPoint), &point->color); + glNormalPointer(GL_BYTE, sizeof(VoxelPoint), &point->normal); + + glDrawRangeElements(GL_QUADS, 0, batch.vertexCount - 1, batch.indexCount, GL_UNSIGNED_INT, 0); + + batch.vertexBuffer->release(); + batch.indexBuffer->release(); + } + + _baseVoxelProgram.release(); + + glDisable(GL_ALPHA_TEST); + glEnable(GL_BLEND); + + Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false); + + if (!_voxelSplatBatches.isEmpty()) { + glDepthFunc(GL_LEQUAL); + glDepthMask(false); + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(-1.0f, -1.0f); + + _splatVoxelProgram.bind(); + + _splatVoxelProgram.enableAttributeArray(_splatVoxelLocations.materials); + _splatVoxelProgram.enableAttributeArray(_splatVoxelLocations.materialWeights); + + foreach (const VoxelSplatBatch& batch, _voxelSplatBatches) { + batch.vertexBuffer->bind(); + batch.indexBuffer->bind(); + + VoxelPoint* point = 0; + glVertexPointer(3, GL_FLOAT, sizeof(VoxelPoint), &point->vertex); + glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VoxelPoint), &point->color); + glNormalPointer(GL_BYTE, sizeof(VoxelPoint), &point->normal); + + _splatVoxelProgram.setAttributeBuffer(_splatVoxelLocations.materials, + GL_UNSIGNED_BYTE, (qint64)&point->materials, SPLAT_COUNT, sizeof(VoxelPoint)); + _splatVoxelProgram.setAttributeBuffer(_splatVoxelLocations.materialWeights, + GL_UNSIGNED_BYTE, (qint64)&point->materialWeights, SPLAT_COUNT, sizeof(VoxelPoint)); + + const float QUARTER_STEP = 0.25f * EIGHT_BIT_MAXIMUM_RECIPROCAL; + _splatVoxelProgram.setUniform(_splatVoxelLocations.splatTextureScalesS, batch.splatTextureScalesS); + _splatVoxelProgram.setUniform(_splatVoxelLocations.splatTextureScalesT, batch.splatTextureScalesT); + _splatVoxelProgram.setUniformValue( + _splatVoxelLocations.textureValueMinima, + (batch.materialIndex + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, + (batch.materialIndex + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, + (batch.materialIndex + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, + (batch.materialIndex + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP); + _splatVoxelProgram.setUniformValue( + _splatVoxelLocations.textureValueMaxima, + (batch.materialIndex + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, + (batch.materialIndex + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, + (batch.materialIndex + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, + (batch.materialIndex + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP); + + for (int i = 0; i < SPLAT_COUNT; i++) { + glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[i]); + glBindTexture(GL_TEXTURE_2D, batch.splatTextureIDs[i]); + } + + glDrawRangeElements(GL_QUADS, 0, batch.vertexCount - 1, batch.indexCount, GL_UNSIGNED_INT, 0); + + for (int i = 0; i < SPLAT_COUNT; i++) { + glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[i]); + glBindTexture(GL_TEXTURE_2D, 0); + } + + glActiveTexture(GL_TEXTURE0); + + batch.vertexBuffer->release(); + batch.indexBuffer->release(); + } + + glDisable(GL_POLYGON_OFFSET_FILL); + glDepthMask(true); + glDepthFunc(GL_LESS); + + _splatVoxelProgram.disableAttributeArray(_splatVoxelLocations.materials); + _splatVoxelProgram.disableAttributeArray(_splatVoxelLocations.materialWeights); + + _voxelSplatBatches.clear(); + } + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + glDisable(GL_CULL_FACE); + + _voxelBaseBatches.clear(); + } + // give external parties a chance to join in emit rendering(); } @@ -174,185 +477,6 @@ void MetavoxelSystem::refreshVoxelData() { } } -class RayHeightfieldIntersectionVisitor : public RayIntersectionVisitor { -public: - - float intersectionDistance; - - RayHeightfieldIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const MetavoxelLOD& lod); - - virtual int visit(MetavoxelInfo& info, float distance); -}; - -RayHeightfieldIntersectionVisitor::RayHeightfieldIntersectionVisitor(const glm::vec3& origin, - const glm::vec3& direction, const MetavoxelLOD& lod) : - RayIntersectionVisitor(origin, direction, QVector() << - Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector(), lod), - intersectionDistance(FLT_MAX) { -} - -static const int EIGHT_BIT_MAXIMUM = 255; -static const float EIGHT_BIT_MAXIMUM_RECIPROCAL = 1.0f / EIGHT_BIT_MAXIMUM; - -int RayHeightfieldIntersectionVisitor::visit(MetavoxelInfo& info, float distance) { - if (!info.isLeaf) { - return _order; - } - const HeightfieldBuffer* buffer = static_cast( - info.inputValues.at(0).getInlineValue().data()); - if (!buffer) { - return STOP_RECURSION; - } - const QByteArray& contents = buffer->getHeight(); - const uchar* src = (const uchar*)contents.constData(); - int size = glm::sqrt((float)contents.size()); - int unextendedSize = size - HeightfieldBuffer::HEIGHT_EXTENSION; - int highest = HeightfieldBuffer::HEIGHT_BORDER + unextendedSize; - float heightScale = unextendedSize * EIGHT_BIT_MAXIMUM_RECIPROCAL; - - // find the initial location in heightfield coordinates - glm::vec3 entry = (_origin + distance * _direction - info.minimum) * (float)unextendedSize / info.size; - entry.x += HeightfieldBuffer::HEIGHT_BORDER; - entry.z += HeightfieldBuffer::HEIGHT_BORDER; - 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; - while (withinBounds) { - // find the heights at the corners of the current cell - int floorX = qMin(qMax((int)floors.x, HeightfieldBuffer::HEIGHT_BORDER), highest); - int floorZ = qMin(qMax((int)floors.z, HeightfieldBuffer::HEIGHT_BORDER), highest); - int ceilX = qMin(qMax((int)ceils.x, HeightfieldBuffer::HEIGHT_BORDER), highest); - int ceilZ = qMin(qMax((int)ceils.z, HeightfieldBuffer::HEIGHT_BORDER), highest); - float upperLeft = src[floorZ * size + floorX] * heightScale; - float upperRight = src[floorZ * size + ceilX] * heightScale; - float lowerLeft = src[ceilZ * size + floorX] * heightScale; - float lowerRight = src[ceilZ * size + 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) { - if (_direction.y > 0.0f) { - return SHORT_CIRCUIT; // line points upwards; no collisions possible - } - withinBounds = false; // line points 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 >= HeightfieldBuffer::HEIGHT_BORDER && exit.y <= highest); - if (exitDistance == xDistance) { - if (_direction.x > 0.0f) { - nextFloors.x += 1.0f; - withinBounds &= (nextCeils.x += 1.0f) <= highest; - } else { - withinBounds &= (nextFloors.x -= 1.0f) >= HeightfieldBuffer::HEIGHT_BORDER; - nextCeils.x -= 1.0f; - } - } - if (exitDistance == zDistance) { - if (_direction.z > 0.0f) { - nextFloors.z += 1.0f; - withinBounds &= (nextCeils.z += 1.0f) <= highest; - } else { - withinBounds &= (nextFloors.z -= 1.0f) >= HeightfieldBuffer::HEIGHT_BORDER; - 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) { - intersectionDistance = qMin(intersectionDistance, distance + - (accumulatedDistance + planeDistance) * (info.size / unextendedSize)); - return SHORT_CIRCUIT; - } - } - - // 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) { - intersectionDistance = qMin(intersectionDistance, distance + - (accumulatedDistance + planeDistance) * (info.size / unextendedSize)); - return SHORT_CIRCUIT; - } - } - - // no joy; continue on our way - entry = exit; - floors = nextFloors; - ceils = nextCeils; - accumulatedDistance += exitDistance; - } - - return STOP_RECURSION; -} - -bool MetavoxelSystem::findFirstRayHeightfieldIntersection(const glm::vec3& origin, - const glm::vec3& direction, float& distance) { - RayHeightfieldIntersectionVisitor visitor(origin, direction, getLOD()); - guideToAugmented(visitor); - if (visitor.intersectionDistance == FLT_MAX) { - return false; - } - distance = visitor.intersectionDistance; - return true; -} - class RayVoxelIntersectionVisitor : public RayIntersectionVisitor { public: @@ -396,89 +520,6 @@ bool MetavoxelSystem::findFirstRayVoxelIntersection(const glm::vec3& origin, con return true; } -class HeightfieldHeightVisitor : public MetavoxelVisitor { -public: - - float height; - - HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location); - - virtual int visit(MetavoxelInfo& info); - -private: - - glm::vec3 _location; -}; - -HeightfieldHeightVisitor::HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location) : - MetavoxelVisitor(QVector() << - Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector(), lod), - height(-FLT_MAX), - _location(location) { -} - -static const int REVERSE_ORDER = MetavoxelVisitor::encodeOrder(7, 6, 5, 4, 3, 2, 1, 0); - -int HeightfieldHeightVisitor::visit(MetavoxelInfo& info) { - glm::vec3 relative = _location - info.minimum; - if (relative.x < 0.0f || relative.z < 0.0f || relative.x > info.size || relative.z > info.size || - height >= info.minimum.y + info.size) { - return STOP_RECURSION; - } - if (!info.isLeaf) { - return REVERSE_ORDER; - } - const HeightfieldBuffer* buffer = static_cast( - info.inputValues.at(0).getInlineValue().data()); - if (!buffer) { - return STOP_RECURSION; - } - const QByteArray& contents = buffer->getHeight(); - const uchar* src = (const uchar*)contents.constData(); - int size = glm::sqrt((float)contents.size()); - int unextendedSize = size - HeightfieldBuffer::HEIGHT_EXTENSION; - int highest = HeightfieldBuffer::HEIGHT_BORDER + unextendedSize; - relative *= unextendedSize / info.size; - relative.x += HeightfieldBuffer::HEIGHT_BORDER; - relative.z += HeightfieldBuffer::HEIGHT_BORDER; - - // find the bounds of the cell containing the point and the shared vertex heights - glm::vec3 floors = glm::floor(relative); - glm::vec3 ceils = glm::ceil(relative); - glm::vec3 fracts = glm::fract(relative); - int floorX = qMin(qMax((int)floors.x, HeightfieldBuffer::HEIGHT_BORDER), highest); - int floorZ = qMin(qMax((int)floors.z, HeightfieldBuffer::HEIGHT_BORDER), highest); - int ceilX = qMin(qMax((int)ceils.x, HeightfieldBuffer::HEIGHT_BORDER), highest); - int ceilZ = qMin(qMax((int)ceils.z, HeightfieldBuffer::HEIGHT_BORDER), highest); - float upperLeft = src[floorZ * size + floorX]; - float lowerRight = src[ceilZ * size + 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 * size + ceilX]; - interpolatedHeight = glm::mix(interpolatedHeight, glm::mix(upperRight, lowerRight, fracts.z), - (fracts.x - fracts.z) / (1.0f - fracts.z)); - - } else { - float lowerLeft = src[ceilZ * size + floorX]; - interpolatedHeight = glm::mix(glm::mix(upperLeft, lowerLeft, fracts.z), interpolatedHeight, fracts.x / fracts.z); - } - if (interpolatedHeight == 0.0f) { - return STOP_RECURSION; // ignore zero values - } - - // convert the interpolated height into world space - height = qMax(height, info.minimum.y + interpolatedHeight * info.size * EIGHT_BIT_MAXIMUM_RECIPROCAL); - return SHORT_CIRCUIT; -} - -float MetavoxelSystem::getHeightfieldHeight(const glm::vec3& location) { - HeightfieldHeightVisitor visitor(getLOD(), location); - guideToAugmented(visitor); - return visitor.height; -} - void MetavoxelSystem::paintHeightfieldColor(const glm::vec3& position, float radius, const QColor& color) { MetavoxelEditMessage edit = { QVariant::fromValue(PaintHeightfieldMaterialEdit(position, radius, SharedObjectPointer(), color)) }; applyEdit(edit, true); @@ -509,10 +550,12 @@ void MetavoxelSystem::setVoxelMaterial(const SharedObjectPointer& spanner, const applyMaterialEdit(edit, true); } -class CursorRenderVisitor : public MetavoxelVisitor { +class SpannerCursorRenderVisitor : public SpannerVisitor { public: - CursorRenderVisitor(const AttributePointer& attribute, const Box& bounds); + SpannerCursorRenderVisitor(const Box& bounds); + + virtual bool visit(Spanner* spanner); virtual int visit(MetavoxelInfo& info); @@ -521,23 +564,20 @@ private: Box _bounds; }; -CursorRenderVisitor::CursorRenderVisitor(const AttributePointer& attribute, const Box& bounds) : - MetavoxelVisitor(QVector() << attribute), +SpannerCursorRenderVisitor::SpannerCursorRenderVisitor(const Box& bounds) : + SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute()), _bounds(bounds) { } -int CursorRenderVisitor::visit(MetavoxelInfo& info) { - if (!info.getBounds().intersects(_bounds)) { - return STOP_RECURSION; +bool SpannerCursorRenderVisitor::visit(Spanner* spanner) { + if (spanner->isHeightfield()) { + spanner->getRenderer()->render(true); } - if (!info.isLeaf) { - return DEFAULT_ORDER; - } - BufferDataPointer buffer = info.inputValues.at(0).getInlineValue(); - if (buffer) { - buffer->render(true); - } - return STOP_RECURSION; + return true; +} + +int SpannerCursorRenderVisitor::visit(MetavoxelInfo& info) { + return info.getBounds().intersects(_bounds) ? SpannerVisitor::visit(info) : STOP_RECURSION; } void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float radius) { @@ -551,7 +591,7 @@ void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float r glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); - DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().bind(); + _heightfieldCursorProgram.bind(); glActiveTexture(GL_TEXTURE4); float scale = 1.0f / radius; @@ -564,11 +604,10 @@ void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float r glActiveTexture(GL_TEXTURE0); glm::vec3 extents(radius, radius, radius); - CursorRenderVisitor visitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), - Box(position - extents, position + extents)); - guideToAugmented(visitor); + SpannerCursorRenderVisitor visitor(Box(position - extents, position + extents)); + guide(visitor); - DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().release(); + _heightfieldCursorProgram.release(); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); @@ -578,6 +617,34 @@ void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float r glDepthFunc(GL_LESS); } +class BufferCursorRenderVisitor : public MetavoxelVisitor { +public: + + BufferCursorRenderVisitor(const AttributePointer& attribute, const Box& bounds); + + virtual int visit(MetavoxelInfo& info); + +private: + + Box _bounds; +}; + +BufferCursorRenderVisitor::BufferCursorRenderVisitor(const AttributePointer& attribute, const Box& bounds) : + MetavoxelVisitor(QVector() << attribute), + _bounds(bounds) { +} + +int BufferCursorRenderVisitor::visit(MetavoxelInfo& info) { + if (!info.getBounds().intersects(_bounds)) { + return STOP_RECURSION; + } + BufferData* buffer = info.inputValues.at(0).getInlineValue().data(); + if (buffer) { + buffer->render(true); + } + return info.isLeaf ? STOP_RECURSION : DEFAULT_ORDER; +} + void MetavoxelSystem::renderVoxelCursor(const glm::vec3& position, float radius) { glDepthFunc(GL_LEQUAL); glEnable(GL_CULL_FACE); @@ -588,7 +655,7 @@ void MetavoxelSystem::renderVoxelCursor(const glm::vec3& position, float radius) glEnableClientState(GL_VERTEX_ARRAY); - DefaultMetavoxelRendererImplementation::getVoxelCursorProgram().bind(); + _voxelCursorProgram.bind(); glActiveTexture(GL_TEXTURE4); float scale = 1.0f / radius; @@ -602,22 +669,22 @@ void MetavoxelSystem::renderVoxelCursor(const glm::vec3& position, float radius) glm::vec3 extents(radius, radius, radius); Box bounds(position - extents, position + extents); - CursorRenderVisitor voxelVisitor(Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(), bounds); + BufferCursorRenderVisitor voxelVisitor(Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(), bounds); guideToAugmented(voxelVisitor); - DefaultMetavoxelRendererImplementation::getVoxelCursorProgram().release(); + _voxelCursorProgram.release(); glEnableClientState(GL_TEXTURE_COORD_ARRAY); - DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().bind(); + _heightfieldCursorProgram.bind(); - CursorRenderVisitor heightfieldVisitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), - bounds); - guideToAugmented(heightfieldVisitor); + SpannerCursorRenderVisitor spannerVisitor(bounds); + guide(spannerVisitor); - DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().release(); + _heightfieldCursorProgram.release(); glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); glDisable(GL_POLYGON_OFFSET_FILL); @@ -625,12 +692,6 @@ void MetavoxelSystem::renderVoxelCursor(const glm::vec3& position, float radius) glDepthFunc(GL_LESS); } -void MetavoxelSystem::deleteTextures(int heightID, int colorID, int textureID) { - glDeleteTextures(1, (GLuint*)&heightID); - glDeleteTextures(1, (GLuint*)&colorID); - glDeleteTextures(1, (GLuint*)&textureID); -} - class MaterialEditApplier : public SignalHandler { public: @@ -702,6 +763,29 @@ void MetavoxelSystem::guideToAugmented(MetavoxelVisitor& visitor, bool render) { } } +void MetavoxelSystem::loadSplatProgram(const char* type, ProgramObject& program, SplatLocations& locations) { + program.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + + "shaders/metavoxel_" + type + "_splat.vert"); + program.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + + "shaders/metavoxel_" + type + "_splat.frag"); + program.link(); + + program.bind(); + program.setUniformValue("heightMap", 0); + program.setUniformValue("textureMap", 1); + program.setUniformValueArray("diffuseMaps", SPLAT_TEXTURE_UNITS, SPLAT_COUNT); + locations.heightScale = program.uniformLocation("heightScale"); + locations.textureScale = program.uniformLocation("textureScale"); + locations.splatTextureOffset = program.uniformLocation("splatTextureOffset"); + locations.splatTextureScalesS = program.uniformLocation("splatTextureScalesS"); + locations.splatTextureScalesT = program.uniformLocation("splatTextureScalesT"); + locations.textureValueMinima = program.uniformLocation("textureValueMinima"); + locations.textureValueMaxima = program.uniformLocation("textureValueMaxima"); + locations.materials = program.attributeLocation("materials"); + locations.materialWeights = program.attributeLocation("materialWeights"); + program.release(); +} + Throttle::Throttle() : _limit(INT_MAX), _total(0) { @@ -920,384 +1004,6 @@ void MetavoxelSystemClient::sendDatagram(const QByteArray& data) { BufferData::~BufferData() { } -PointBuffer::PointBuffer(const BufferPointVector& points) : - _points(points) { -} - -void PointBuffer::render(bool cursor) { - // initialize buffer, etc. on first render - if (!_buffer.isCreated()) { - _buffer.setUsagePattern(QOpenGLBuffer::StaticDraw); - _buffer.create(); - _buffer.bind(); - _pointCount = _points.size(); - _buffer.allocate(_points.constData(), _pointCount * sizeof(BufferPoint)); - _points.clear(); - _buffer.release(); - } - if (_pointCount == 0) { - return; - } - _buffer.bind(); - - BufferPoint* point = 0; - glVertexPointer(4, GL_FLOAT, sizeof(BufferPoint), &point->vertex); - glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(BufferPoint), &point->color); - glNormalPointer(GL_BYTE, sizeof(BufferPoint), &point->normal); - - glDrawArrays(GL_POINTS, 0, _pointCount); - - _buffer.release(); -} - -const int HeightfieldBuffer::HEIGHT_BORDER = 1; -const int HeightfieldBuffer::SHARED_EDGE = 1; -const int HeightfieldBuffer::HEIGHT_EXTENSION = 2 * HeightfieldBuffer::HEIGHT_BORDER + HeightfieldBuffer::SHARED_EDGE; - -HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale, - const QByteArray& height, const QByteArray& color, const QByteArray& material, - const QVector& materials) : - _translation(translation), - _scale(scale), - _heightBounds(translation, translation + glm::vec3(scale, scale, scale)), - _colorBounds(_heightBounds), - _materialBounds(_heightBounds), - _height(height), - _color(color), - _material(material), - _materials(materials), - _heightTextureID(0), - _colorTextureID(0), - _materialTextureID(0), - _heightSize(glm::sqrt((float)height.size())), - _heightIncrement(scale / (_heightSize - HEIGHT_EXTENSION)), - _colorSize(glm::sqrt((float)color.size() / DataBlock::COLOR_BYTES)), - _colorIncrement(scale / (_colorSize - SHARED_EDGE)), - _materialSize(glm::sqrt((float)material.size())), - _materialIncrement(scale / (_materialSize - SHARED_EDGE)) { - - _heightBounds.minimum.x -= _heightIncrement * HEIGHT_BORDER; - _heightBounds.minimum.z -= _heightIncrement * HEIGHT_BORDER; - _heightBounds.maximum.x += _heightIncrement * (SHARED_EDGE + HEIGHT_BORDER); - _heightBounds.maximum.z += _heightIncrement * (SHARED_EDGE + HEIGHT_BORDER); - - _colorBounds.maximum.x += _colorIncrement * SHARED_EDGE; - _colorBounds.maximum.z += _colorIncrement * SHARED_EDGE; - - _materialBounds.maximum.x += _materialIncrement * SHARED_EDGE; - _materialBounds.maximum.z += _materialIncrement * SHARED_EDGE; -} - -HeightfieldBuffer::~HeightfieldBuffer() { - // the textures have to be deleted on the main thread (for its opengl context) - if (QThread::currentThread() != Application::getInstance()->thread()) { - QMetaObject::invokeMethod(Application::getInstance()->getMetavoxels(), "deleteTextures", - Q_ARG(int, _heightTextureID), Q_ARG(int, _colorTextureID), Q_ARG(int, _materialTextureID)); - } else { - glDeleteTextures(1, &_heightTextureID); - glDeleteTextures(1, &_colorTextureID); - glDeleteTextures(1, &_materialTextureID); - } -} - -QByteArray HeightfieldBuffer::getUnextendedHeight() const { - int srcSize = glm::sqrt(float(_height.size())); - int destSize = srcSize - 3; - QByteArray unextended(destSize * destSize, 0); - const char* src = _height.constData() + srcSize + 1; - char* dest = unextended.data(); - for (int z = 0; z < destSize; z++, src += srcSize, dest += destSize) { - memcpy(dest, src, destSize); - } - return unextended; -} - -QByteArray HeightfieldBuffer::getUnextendedColor(int x, int y) const { - int unextendedSize = _heightSize - HEIGHT_EXTENSION; - QByteArray unextended(unextendedSize * unextendedSize * DataBlock::COLOR_BYTES, 0); - char* dest = unextended.data(); - const char* src = _color.constData() + (y * _colorSize + x) * unextendedSize * DataBlock::COLOR_BYTES; - for (int z = 0; z < unextendedSize; z++, dest += unextendedSize * DataBlock::COLOR_BYTES, - src += _colorSize * DataBlock::COLOR_BYTES) { - memcpy(dest, src, unextendedSize * DataBlock::COLOR_BYTES); - } - return unextended; -} - -class HeightfieldPoint { -public: - glm::vec2 textureCoord; - glm::vec3 vertex; -}; - -const int SPLAT_COUNT = 4; -const GLint SPLAT_TEXTURE_UNITS[] = { 3, 4, 5, 6 }; - -void HeightfieldBuffer::render(bool cursor) { - // initialize textures, etc. on first render - if (_heightTextureID == 0) { - glGenTextures(1, &_heightTextureID); - glBindTexture(GL_TEXTURE_2D, _heightTextureID); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, _heightSize, _heightSize, 0, - GL_LUMINANCE, GL_UNSIGNED_BYTE, _height.constData()); - - glGenTextures(1, &_colorTextureID); - glBindTexture(GL_TEXTURE_2D, _colorTextureID); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - if (_color.isEmpty()) { - const quint8 WHITE_COLOR[] = { 255, 255, 255 }; - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, WHITE_COLOR); - - } else { - int colorSize = glm::sqrt(float(_color.size() / DataBlock::COLOR_BYTES)); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, colorSize, colorSize, 0, GL_RGB, GL_UNSIGNED_BYTE, _color.constData()); - } - - if (!_material.isEmpty()) { - glGenTextures(1, &_materialTextureID); - glBindTexture(GL_TEXTURE_2D, _materialTextureID); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - int materialSize = glm::sqrt(float(_material.size())); - glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, materialSize, materialSize, 0, - GL_LUMINANCE, GL_UNSIGNED_BYTE, _material.constData()); - - _networkTextures.resize(_materials.size()); - for (int i = 0; i < _materials.size(); i++) { - const SharedObjectPointer material = _materials.at(i); - if (material) { - _networkTextures[i] = Application::getInstance()->getTextureCache()->getTexture( - static_cast(material.data())->getDiffuse(), SPLAT_TEXTURE); - } - } - } - } - // create the buffer objects lazily - int innerSize = _heightSize - 2 * HeightfieldBuffer::HEIGHT_BORDER; - int vertexCount = _heightSize * _heightSize; - int rows = _heightSize - 1; - int indexCount = rows * rows * 3 * 2; - BufferPair& bufferPair = _bufferPairs[_heightSize]; - if (!bufferPair.first.isCreated()) { - QVector vertices(vertexCount); - HeightfieldPoint* point = vertices.data(); - - float vertexStep = 1.0f / (innerSize - 1); - float z = -vertexStep; - float textureStep = 1.0f / _heightSize; - float t = textureStep / 2.0f; - for (int i = 0; i < _heightSize; i++, z += vertexStep, t += textureStep) { - float x = -vertexStep; - float s = textureStep / 2.0f; - const float SKIRT_LENGTH = 0.25f; - float baseY = (i == 0 || i == _heightSize - 1) ? -SKIRT_LENGTH : 0.0f; - for (int j = 0; j < _heightSize; j++, point++, x += vertexStep, s += textureStep) { - point->vertex = glm::vec3(x, (j == 0 || j == _heightSize - 1) ? -SKIRT_LENGTH : baseY, z); - point->textureCoord = glm::vec2(s, t); - } - } - - bufferPair.first.setUsagePattern(QOpenGLBuffer::StaticDraw); - bufferPair.first.create(); - bufferPair.first.bind(); - bufferPair.first.allocate(vertices.constData(), vertexCount * sizeof(HeightfieldPoint)); - - QVector indices(indexCount); - int* index = indices.data(); - for (int i = 0; i < rows; i++) { - int lineIndex = i * _heightSize; - int nextLineIndex = (i + 1) * _heightSize; - for (int j = 0; j < rows; j++) { - *index++ = lineIndex + j; - *index++ = nextLineIndex + j; - *index++ = nextLineIndex + j + 1; - - *index++ = nextLineIndex + j + 1; - *index++ = lineIndex + j + 1; - *index++ = lineIndex + j; - } - } - - bufferPair.second = QOpenGLBuffer(QOpenGLBuffer::IndexBuffer); - bufferPair.second.create(); - bufferPair.second.bind(); - bufferPair.second.allocate(indices.constData(), indexCount * sizeof(int)); - - } else { - bufferPair.first.bind(); - bufferPair.second.bind(); - } - - HeightfieldPoint* point = 0; - glVertexPointer(3, GL_FLOAT, sizeof(HeightfieldPoint), &point->vertex); - glTexCoordPointer(2, GL_FLOAT, sizeof(HeightfieldPoint), &point->textureCoord); - - glPushMatrix(); - glTranslatef(_translation.x, _translation.y, _translation.z); - glScalef(_scale, _scale, _scale); - - glBindTexture(GL_TEXTURE_2D, _heightTextureID); - - if (cursor) { - glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); - - } else if (!_materials.isEmpty()) { - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( - DefaultMetavoxelRendererImplementation::getBaseHeightScaleLocation(), 1.0f / _heightSize); - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( - DefaultMetavoxelRendererImplementation::getBaseColorScaleLocation(), (float)_heightSize / innerSize); - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, _colorTextureID); - - glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); - - Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false); - - glDepthFunc(GL_LEQUAL); - glDepthMask(false); - glEnable(GL_BLEND); - glDisable(GL_ALPHA_TEST); - glEnable(GL_POLYGON_OFFSET_FILL); - glPolygonOffset(-1.0f, -1.0f); - - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().bind(); - const DefaultMetavoxelRendererImplementation::SplatLocations& locations = - DefaultMetavoxelRendererImplementation::getSplatHeightfieldLocations(); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.heightScale, 1.0f / _heightSize); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.textureScale, (float)_heightSize / innerSize); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.splatTextureOffset, _translation.x / _scale, _translation.z / _scale); - - glBindTexture(GL_TEXTURE_2D, _materialTextureID); - - for (int i = 0; i < _materials.size(); i += SPLAT_COUNT) { - QVector4D scalesS, scalesT; - - for (int j = 0; j < SPLAT_COUNT; j++) { - glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[j]); - int index = i + j; - if (index < _networkTextures.size()) { - const NetworkTexturePointer& texture = _networkTextures.at(index); - if (texture) { - MaterialObject* material = static_cast(_materials.at(index).data()); - scalesS[j] = _scale / material->getScaleS(); - scalesT[j] = _scale / material->getScaleT(); - glBindTexture(GL_TEXTURE_2D, texture->getID()); - } else { - glBindTexture(GL_TEXTURE_2D, 0); - } - } else { - glBindTexture(GL_TEXTURE_2D, 0); - } - } - const float QUARTER_STEP = 0.25f * EIGHT_BIT_MAXIMUM_RECIPROCAL; - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.splatTextureScalesS, scalesS); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.splatTextureScalesT, scalesT); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.textureValueMinima, - (i + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, (i + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, - (i + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, (i + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP); - DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - locations.textureValueMaxima, - (i + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, (i + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, - (i + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, (i + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP); - glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); - } - - for (int i = 0; i < SPLAT_COUNT; i++) { - glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[i]); - glBindTexture(GL_TEXTURE_2D, 0); - } - - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, 0); - - glActiveTexture(GL_TEXTURE0); - - glDisable(GL_POLYGON_OFFSET_FILL); - glEnable(GL_ALPHA_TEST); - glDisable(GL_BLEND); - glDepthMask(true); - glDepthFunc(GL_LESS); - - Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, true); - - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().bind(); - - } else { - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( - DefaultMetavoxelRendererImplementation::getBaseHeightScaleLocation(), 1.0f / _heightSize); - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( - DefaultMetavoxelRendererImplementation::getBaseColorScaleLocation(), (float)_heightSize / innerSize); - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, _colorTextureID); - - glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); - - glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE0); - } - - glBindTexture(GL_TEXTURE_2D, 0); - - glPopMatrix(); - - bufferPair.first.release(); - bufferPair.second.release(); -} - -QHash HeightfieldBuffer::_bufferPairs; - -void HeightfieldPreview::render(const glm::vec3& translation, float scale) const { - Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, true); - - glDisable(GL_BLEND); - glEnable(GL_CULL_FACE); - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_EQUAL, 0.0f); - - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().bind(); - - glPushMatrix(); - glTranslatef(translation.x, translation.y, translation.z); - glScalef(scale, scale, scale); - - foreach (const BufferDataPointer& buffer, _buffers) { - buffer->render(); - } - - glPopMatrix(); - - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().release(); - - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - - glDisable(GL_ALPHA_TEST); - glDisable(GL_CULL_FACE); - glEnable(GL_BLEND); - - Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false); -} - void VoxelPoint::setNormal(const glm::vec3& normal) { this->normal[0] = (char)(normal.x * 127.0f); this->normal[1] = (char)(normal.y * 127.0f); @@ -1305,7 +1011,7 @@ void VoxelPoint::setNormal(const glm::vec3& normal) { } VoxelBuffer::VoxelBuffer(const QVector& vertices, const QVector& indices, const QVector& hermite, - const QMultiHash& quadIndices, int size, const QVector& materials) : + const QMultiHash& quadIndices, int size, const QVector& materials) : _vertices(vertices), _indices(indices), _hermite(hermite), @@ -1350,7 +1056,7 @@ bool VoxelBuffer::findFirstRayIntersection(const glm::vec3& entry, const glm::ve int max = _size - 2; int x = qMin((int)floors.x, max), y = qMin((int)floors.y, max), z = qMin((int)floors.z, max); forever { - for (QMultiHash::const_iterator it = _quadIndices.constFind(qRgb(x + 1, y + 1, z + 1)); + for (QMultiHash::const_iterator it = _quadIndices.constFind(qRgb(x + 1, y + 1, z + 1)); it != _quadIndices.constEnd(); it++) { const int* indices = _indices.constData() + *it; if (findRayTriangleIntersection(origin, direction, _vertices.at(indices[0]).vertex, @@ -1414,10 +1120,12 @@ void VoxelBuffer::render(bool cursor) { _vertexBuffer.create(); _vertexBuffer.bind(); _vertexBuffer.allocate(_vertices.constData(), _vertices.size() * sizeof(VoxelPoint)); + _vertexBuffer.release(); _indexBuffer.create(); _indexBuffer.bind(); _indexBuffer.allocate(_indices.constData(), _indices.size() * sizeof(int)); + _indexBuffer.release(); if (!_materials.isEmpty()) { _networkTextures.resize(_materials.size()); @@ -1429,101 +1137,62 @@ void VoxelBuffer::render(bool cursor) { } } } - } else { - _vertexBuffer.bind(); - _indexBuffer.bind(); } - VoxelPoint* point = 0; - glVertexPointer(3, GL_FLOAT, sizeof(VoxelPoint), &point->vertex); - glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VoxelPoint), &point->color); - glNormalPointer(GL_BYTE, sizeof(VoxelPoint), &point->normal); + if (cursor) { + _vertexBuffer.bind(); + _indexBuffer.bind(); + + VoxelPoint* point = 0; + glVertexPointer(3, GL_FLOAT, sizeof(VoxelPoint), &point->vertex); + glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VoxelPoint), &point->color); + glNormalPointer(GL_BYTE, sizeof(VoxelPoint), &point->normal); + + glDrawRangeElements(GL_QUADS, 0, _vertexCount - 1, _indexCount, GL_UNSIGNED_INT, 0); + + _vertexBuffer.release(); + _indexBuffer.release(); + return; + } - glDrawRangeElements(GL_QUADS, 0, _vertexCount - 1, _indexCount, GL_UNSIGNED_INT, 0); + VoxelBatch baseBatch; + baseBatch.vertexBuffer = &_vertexBuffer; + baseBatch.indexBuffer = &_indexBuffer; + baseBatch.vertexCount = _vertexCount; + baseBatch.indexCount = _indexCount; + Application::getInstance()->getMetavoxels()->addVoxelBaseBatch(baseBatch); + + if (!_materials.isEmpty()) { + VoxelSplatBatch splatBatch; + splatBatch.vertexBuffer = &_vertexBuffer; + splatBatch.indexBuffer = &_indexBuffer; + splatBatch.vertexCount = _vertexCount; + splatBatch.indexCount = _indexCount; - if (!(_materials.isEmpty() || cursor)) { - Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false); - - glDepthFunc(GL_LEQUAL); - glDepthMask(false); - glEnable(GL_BLEND); - glDisable(GL_ALPHA_TEST); - glEnable(GL_POLYGON_OFFSET_FILL); - glPolygonOffset(-1.0f, -1.0f); - - DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().bind(); - const DefaultMetavoxelRendererImplementation::SplatLocations& locations = - DefaultMetavoxelRendererImplementation::getSplatVoxelLocations(); - - DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().setAttributeBuffer(locations.materials, - GL_UNSIGNED_BYTE, (qint64)&point->materials, SPLAT_COUNT, sizeof(VoxelPoint)); - DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().enableAttributeArray(locations.materials); - - DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().setAttributeBuffer(locations.materialWeights, - GL_UNSIGNED_BYTE, (qint64)&point->materialWeights, SPLAT_COUNT, sizeof(VoxelPoint)); - DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().enableAttributeArray(locations.materialWeights); - for (int i = 0; i < _materials.size(); i += SPLAT_COUNT) { - QVector4D scalesS, scalesT; - for (int j = 0; j < SPLAT_COUNT; j++) { - glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[j]); int index = i + j; if (index < _networkTextures.size()) { const NetworkTexturePointer& texture = _networkTextures.at(index); if (texture) { MaterialObject* material = static_cast(_materials.at(index).data()); - scalesS[j] = 1.0f / material->getScaleS(); - scalesT[j] = 1.0f / material->getScaleT(); - glBindTexture(GL_TEXTURE_2D, texture->getID()); + splatBatch.splatTextureScalesS[j] = 1.0f / material->getScaleS(); + splatBatch.splatTextureScalesT[j] = 1.0f / material->getScaleT(); + splatBatch.splatTextureIDs[j] = texture->getID(); + } else { - glBindTexture(GL_TEXTURE_2D, 0); + splatBatch.splatTextureIDs[j] = 0; } } else { - glBindTexture(GL_TEXTURE_2D, 0); + splatBatch.splatTextureIDs[j] = 0; } } - const float QUARTER_STEP = 0.25f * EIGHT_BIT_MAXIMUM_RECIPROCAL; - DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().setUniformValue( - locations.splatTextureScalesS, scalesS); - DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().setUniformValue( - locations.splatTextureScalesT, scalesT); - DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().setUniformValue( - locations.textureValueMinima, - (i + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, (i + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, - (i + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, (i + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP); - DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().setUniformValue( - locations.textureValueMaxima, - (i + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, (i + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, - (i + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, (i + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP); - glDrawRangeElements(GL_QUADS, 0, _vertexCount - 1, _indexCount, GL_UNSIGNED_INT, 0); + splatBatch.materialIndex = i; + Application::getInstance()->getMetavoxels()->addVoxelSplatBatch(splatBatch); } - - for (int i = 0; i < SPLAT_COUNT; i++) { - glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[i]); - glBindTexture(GL_TEXTURE_2D, 0); - } - - glActiveTexture(GL_TEXTURE0); - - glDisable(GL_POLYGON_OFFSET_FILL); - glEnable(GL_ALPHA_TEST); - glDisable(GL_BLEND); - glDepthMask(true); - glDepthFunc(GL_LESS); - - Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, true); - - DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().disableAttributeArray(locations.materials); - DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().disableAttributeArray(locations.materialWeights); - - DefaultMetavoxelRendererImplementation::getBaseVoxelProgram().bind(); } - _vertexBuffer.release(); - _indexBuffer.release(); - - if (_hermiteCount > 0 && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHermiteData) && !cursor) { + if (_hermiteCount > 0 && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHermiteData)) { if (!_hermiteBuffer.isCreated()) { _hermiteBuffer.create(); _hermiteBuffer.bind(); @@ -1534,9 +1203,6 @@ void VoxelBuffer::render(bool cursor) { _hermiteBuffer.bind(); } - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_NORMAL_ARRAY); - glVertexPointer(3, GL_FLOAT, 0, 0); Application::getInstance()->getDeferredLightingEffect()->getSimpleProgram().bind(); @@ -1548,10 +1214,7 @@ void VoxelBuffer::render(bool cursor) { glDrawArrays(GL_LINES, 0, _hermiteCount); - DefaultMetavoxelRendererImplementation::getBaseVoxelProgram().bind(); - - glEnableClientState(GL_COLOR_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); + Application::getInstance()->getDeferredLightingEffect()->getSimpleProgram().release(); _hermiteBuffer.release(); } @@ -1575,572 +1238,9 @@ AttributeValue BufferDataAttribute::inherit(const AttributeValue& parentValue) c return AttributeValue(parentValue.getAttribute()); } -void DefaultMetavoxelRendererImplementation::init() { - if (!_pointProgram.isLinked()) { - _pointProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + - "shaders/metavoxel_point.vert"); - _pointProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + - "shaders/metavoxel_voxel_base.frag"); - _pointProgram.link(); - - _pointProgram.bind(); - _pointScaleLocation = _pointProgram.uniformLocation("pointScale"); - _pointProgram.release(); - - _baseHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + - "shaders/metavoxel_heightfield_base.vert"); - _baseHeightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + - "shaders/metavoxel_heightfield_base.frag"); - _baseHeightfieldProgram.link(); - - _baseHeightfieldProgram.bind(); - _baseHeightfieldProgram.setUniformValue("heightMap", 0); - _baseHeightfieldProgram.setUniformValue("diffuseMap", 1); - _baseHeightScaleLocation = _baseHeightfieldProgram.uniformLocation("heightScale"); - _baseColorScaleLocation = _baseHeightfieldProgram.uniformLocation("colorScale"); - _baseHeightfieldProgram.release(); - - loadSplatProgram("heightfield", _splatHeightfieldProgram, _splatHeightfieldLocations); - - _heightfieldCursorProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + - "shaders/metavoxel_heightfield_cursor.vert"); - _heightfieldCursorProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + - "shaders/metavoxel_cursor.frag"); - _heightfieldCursorProgram.link(); - - _heightfieldCursorProgram.bind(); - _heightfieldCursorProgram.setUniformValue("heightMap", 0); - _heightfieldCursorProgram.release(); - - _baseVoxelProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + - "shaders/metavoxel_voxel_base.vert"); - _baseVoxelProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + - "shaders/metavoxel_voxel_base.frag"); - _baseVoxelProgram.link(); - - loadSplatProgram("voxel", _splatVoxelProgram, _splatVoxelLocations); - - _voxelCursorProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + - "shaders/metavoxel_voxel_cursor.vert"); - _voxelCursorProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + - "shaders/metavoxel_cursor.frag"); - _voxelCursorProgram.link(); - } -} - DefaultMetavoxelRendererImplementation::DefaultMetavoxelRendererImplementation() { } -class PointAugmentVisitor : public MetavoxelVisitor { -public: - - PointAugmentVisitor(const MetavoxelLOD& lod); - - virtual void prepare(MetavoxelData* data); - virtual int visit(MetavoxelInfo& info); - virtual bool postVisit(MetavoxelInfo& info); - -private: - - BufferPointVector _points; - float _pointLeafSize; -}; - -PointAugmentVisitor::PointAugmentVisitor(const MetavoxelLOD& lod) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getColorAttribute() << - AttributeRegistry::getInstance()->getNormalAttribute(), QVector() << - Application::getInstance()->getMetavoxels()->getPointBufferAttribute(), lod) { -} - -const int ALPHA_RENDER_THRESHOLD = 0; - -void PointAugmentVisitor::prepare(MetavoxelData* data) { - MetavoxelVisitor::prepare(data); - const float MAX_POINT_LEAF_SIZE = 64.0f; - _pointLeafSize = qMin(data->getSize(), MAX_POINT_LEAF_SIZE); -} - -int PointAugmentVisitor::visit(MetavoxelInfo& info) { - if (!info.isLeaf) { - return (info.size > _pointLeafSize) ? DEFAULT_ORDER : (DEFAULT_ORDER | ALL_NODES_REST); - } - QRgb color = info.inputValues.at(0).getInlineValue(); - quint8 alpha = qAlpha(color); - if (alpha > ALPHA_RENDER_THRESHOLD) { - QRgb normal = info.inputValues.at(1).getInlineValue(); - BufferPoint point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size), - { quint8(qRed(color)), quint8(qGreen(color)), quint8(qBlue(color)) }, - { quint8(qRed(normal)), quint8(qGreen(normal)), quint8(qBlue(normal)) } }; - _points.append(point); - } - if (info.size >= _pointLeafSize) { - PointBuffer* buffer = NULL; - if (!_points.isEmpty()) { - BufferPointVector swapPoints; - _points.swap(swapPoints); - buffer = new PointBuffer(swapPoints); - } - BufferDataPointer pointer(buffer); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer)); - } - return STOP_RECURSION; -} - -bool PointAugmentVisitor::postVisit(MetavoxelInfo& info) { - if (info.size != _pointLeafSize) { - return false; - } - PointBuffer* buffer = NULL; - if (!_points.isEmpty()) { - BufferPointVector swapPoints; - _points.swap(swapPoints); - buffer = new PointBuffer(swapPoints); - } - BufferDataPointer pointer(buffer); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer)); - return true; -} - -class HeightfieldFetchVisitor : public MetavoxelVisitor { -public: - - HeightfieldFetchVisitor(const MetavoxelLOD& lod, const QVector& intersections); - - void init(HeightfieldBuffer* buffer); - - virtual int visit(MetavoxelInfo& info); - virtual bool postVisit(MetavoxelInfo& info); - -private: - - const QVector& _intersections; - HeightfieldBuffer* _buffer; - - QVector _depthFlags; -}; - -enum DepthFlags { HEIGHT_FLAG = 0x01, COLOR_FLAG = 0x02, MATERIAL_FLAG = 0x04 }; - -HeightfieldFetchVisitor::HeightfieldFetchVisitor(const MetavoxelLOD& lod, const QVector& intersections) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute() << - AttributeRegistry::getInstance()->getHeightfieldColorAttribute() << - AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), QVector(), lod), - _intersections(intersections) { -} - -void HeightfieldFetchVisitor::init(HeightfieldBuffer* buffer) { - _buffer = buffer; -} - -int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) { - if (!info.getBounds().intersects(_buffer->getHeightBounds())) { - return STOP_RECURSION; - } - if (_depthFlags.size() > _depth) { - _depthFlags[_depth] = 0; - } else { - _depthFlags.append(0); - } - if (!info.isLeaf) { - return DEFAULT_ORDER; - } - postVisit(info); - return STOP_RECURSION; -} - -bool HeightfieldFetchVisitor::postVisit(MetavoxelInfo& info) { - HeightfieldHeightDataPointer height = info.inputValues.at(0).getInlineValue(); - int flags = _depthFlags.at(_depth); - if (height) { - // to handle borders correctly, make sure we only sample nodes with resolution <= ours - int heightSize = glm::sqrt((float)height->getContents().size()); - float heightIncrement = info.size / heightSize; - if (heightIncrement < _buffer->getHeightIncrement() || (flags & HEIGHT_FLAG)) { - height.reset(); - } else { - flags |= HEIGHT_FLAG; - } - } - HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue(); - if (color) { - int colorSize = glm::sqrt((float)color->getContents().size() / DataBlock::COLOR_BYTES); - float colorIncrement = info.size / colorSize; - if (colorIncrement < _buffer->getColorIncrement() || (flags & COLOR_FLAG)) { - color.reset(); - } else { - flags |= COLOR_FLAG; - } - } - HeightfieldMaterialDataPointer material = info.inputValues.at(2).getInlineValue(); - if (material) { - int materialSize = glm::sqrt((float)material->getContents().size()); - float materialIncrement = info.size / materialSize; - if (materialIncrement < _buffer->getMaterialIncrement() || (flags & MATERIAL_FLAG)) { - material.reset(); - } else { - flags |= MATERIAL_FLAG; - } - } - if (_depth > 0) { - _depthFlags[_depth - 1] |= flags; - } - if (!(height || color || material)) { - return false; - } - Box bounds = info.getBounds(); - foreach (const Box& intersection, _intersections) { - Box overlap = intersection.getIntersection(bounds); - if (overlap.isEmpty()) { - continue; - } - if (height) { - float heightIncrement = _buffer->getHeightIncrement(); - const Box& heightBounds = _buffer->getHeightBounds(); - int destX = (overlap.minimum.x - heightBounds.minimum.x) / heightIncrement; - int destY = (overlap.minimum.z - heightBounds.minimum.z) / heightIncrement; - int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / heightIncrement); - int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / heightIncrement); - int heightSize = _buffer->getHeightSize(); - char* dest = _buffer->getHeight().data() + destY * heightSize + destX; - - const QByteArray& srcHeight = height->getContents(); - int srcSize = glm::sqrt((float)srcHeight.size()); - float srcIncrement = info.size / srcSize; - - if (info.size == _buffer->getScale() && srcSize == (heightSize - HeightfieldBuffer::HEIGHT_EXTENSION)) { - // easy case: same resolution - int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; - int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; - - const char* src = srcHeight.constData() + srcY * srcSize + srcX; - for (int y = 0; y < destHeight; y++, src += srcSize, dest += heightSize) { - memcpy(dest, src, destWidth); - } - } else { - // more difficult: different resolutions - float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; - float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; - float srcAdvance = heightIncrement / srcIncrement; - int shift = 0; - float size = _buffer->getScale(); - while (size < info.size) { - shift++; - size *= 2.0f; - } - int subtract = (_buffer->getTranslation().y - info.minimum.y) * EIGHT_BIT_MAXIMUM / _buffer->getScale(); - for (int y = 0; y < destHeight; y++, dest += heightSize, srcY += srcAdvance) { - const uchar* src = (const uchar*)srcHeight.constData() + (int)srcY * srcSize; - float lineSrcX = srcX; - for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrcX += srcAdvance) { - *lineDest = qMin(qMax(0, (src[(int)lineSrcX] << shift) - subtract), EIGHT_BIT_MAXIMUM); - } - } - } - } - if (color) { - const Box& colorBounds = _buffer->getColorBounds(); - overlap = colorBounds.getIntersection(overlap); - float colorIncrement = _buffer->getColorIncrement(); - int destX = (overlap.minimum.x - colorBounds.minimum.x) / colorIncrement; - int destY = (overlap.minimum.z - colorBounds.minimum.z) / colorIncrement; - int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / colorIncrement); - int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / colorIncrement); - int colorSize = _buffer->getColorSize(); - char* dest = _buffer->getColor().data() + (destY * colorSize + destX) * DataBlock::COLOR_BYTES; - int destStride = colorSize * DataBlock::COLOR_BYTES; - int destBytes = destWidth * DataBlock::COLOR_BYTES; - - const QByteArray& srcColor = color->getContents(); - int srcSize = glm::sqrt(float(srcColor.size() / DataBlock::COLOR_BYTES)); - int srcStride = srcSize * DataBlock::COLOR_BYTES; - float srcIncrement = info.size / srcSize; - - if (srcIncrement == colorIncrement) { - // easy case: same resolution - int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; - int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; - - const char* src = srcColor.constData() + (srcY * srcSize + srcX) * DataBlock::COLOR_BYTES; - for (int y = 0; y < destHeight; y++, src += srcStride, dest += destStride) { - memcpy(dest, src, destBytes); - } - } else { - // more difficult: different resolutions - float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; - float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; - float srcAdvance = colorIncrement / srcIncrement; - for (int y = 0; y < destHeight; y++, dest += destStride, srcY += srcAdvance) { - const char* src = srcColor.constData() + (int)srcY * srcStride; - float lineSrcX = srcX; - for (char* lineDest = dest, *end = dest + destBytes; lineDest != end; lineDest += DataBlock::COLOR_BYTES, - lineSrcX += srcAdvance) { - const char* lineSrc = src + (int)lineSrcX * DataBlock::COLOR_BYTES; - lineDest[0] = lineSrc[0]; - lineDest[1] = lineSrc[1]; - lineDest[2] = lineSrc[2]; - } - } - } - } - if (material) { - const Box& materialBounds = _buffer->getMaterialBounds(); - overlap = materialBounds.getIntersection(overlap); - float materialIncrement = _buffer->getMaterialIncrement(); - int destX = (overlap.minimum.x - materialBounds.minimum.x) / materialIncrement; - int destY = (overlap.minimum.z - materialBounds.minimum.z) / materialIncrement; - int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / materialIncrement); - int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / materialIncrement); - int materialSize = _buffer->getMaterialSize(); - char* dest = _buffer->getMaterial().data() + destY * materialSize + destX; - - const QByteArray& srcMaterial = material->getContents(); - const QVector srcMaterials = material->getMaterials(); - int srcSize = glm::sqrt((float)srcMaterial.size()); - float srcIncrement = info.size / srcSize; - QHash materialMappings; - - if (srcIncrement == materialIncrement) { - // easy case: same resolution - int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; - int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; - - const uchar* src = (const uchar*)srcMaterial.constData() + srcY * srcSize + srcX; - for (int y = 0; y < destHeight; y++, src += srcSize, dest += materialSize) { - const uchar* lineSrc = src; - for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrc++) { - int value = *lineSrc; - if (value != 0) { - int& mapping = materialMappings[value]; - if (mapping == 0) { - mapping = getMaterialIndex(material->getMaterials().at(value - 1), - _buffer->getMaterials(), _buffer->getMaterial()); - } - value = mapping; - } - *lineDest = value; - } - } - } else { - // more difficult: different resolutions - float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; - float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; - float srcAdvance = materialIncrement / srcIncrement; - for (int y = 0; y < destHeight; y++, dest += materialSize, srcY += srcAdvance) { - const uchar* src = (const uchar*)srcMaterial.constData() + (int)srcY * srcSize; - float lineSrcX = srcX; - for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrcX += srcAdvance) { - int value = src[(int)lineSrcX]; - if (value != 0) { - int& mapping = materialMappings[value]; - if (mapping == 0) { - mapping = getMaterialIndex(material->getMaterials().at(value - 1), - _buffer->getMaterials(), _buffer->getMaterial()); - } - value = mapping; - } - *lineDest = value; - } - } - } - clearUnusedMaterials(_buffer->getMaterials(), _buffer->getMaterial()); - } - } - return false; -} - -class HeightfieldRegionVisitor : public MetavoxelVisitor { -public: - - QVector regions; - Box regionBounds; - - HeightfieldRegionVisitor(const MetavoxelLOD& lod); - - virtual int visit(MetavoxelInfo& info); - virtual bool postVisit(MetavoxelInfo& info); - -private: - - void addRegion(const Box& unextended, const Box& extended); - - class DepthInfo { - public: - float minimumColorIncrement; - float minimumMaterialIncrement; - - DepthInfo() : minimumColorIncrement(FLT_MAX), minimumMaterialIncrement(FLT_MAX) { } - }; - - QVector _depthInfo; - - QVector _intersections; - HeightfieldFetchVisitor _fetchVisitor; -}; - -HeightfieldRegionVisitor::HeightfieldRegionVisitor(const MetavoxelLOD& lod) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute() << - AttributeRegistry::getInstance()->getHeightfieldColorAttribute() << - AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute() << - Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector() << - Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), lod), - regionBounds(glm::vec3(FLT_MAX, FLT_MAX, FLT_MAX), glm::vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX)), - _fetchVisitor(lod, _intersections) { -} - -int HeightfieldRegionVisitor::visit(MetavoxelInfo& info) { - DepthInfo depthInfo; - HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue(); - if (color) { - int colorSize = glm::sqrt((float)color->getContents().size() / DataBlock::COLOR_BYTES); - depthInfo.minimumColorIncrement = info.size / colorSize; - } - HeightfieldMaterialDataPointer material = info.inputValues.at(2).getInlineValue(); - if (material) { - int materialSize = glm::sqrt((float)material->getContents().size()); - depthInfo.minimumMaterialIncrement = info.size / materialSize; - } - if (_depth < _depthInfo.size()) { - _depthInfo[_depth] = depthInfo; - } else { - _depthInfo.append(depthInfo); - } - if (!info.isLeaf) { - return _visitations.at(_depth).isInputLeaf(0) ? (DEFAULT_ORDER | ALL_NODES_REST) : DEFAULT_ORDER; - } - postVisit(info); - return STOP_RECURSION; -} - -bool HeightfieldRegionVisitor::postVisit(MetavoxelInfo& info) { - const DepthInfo& depthInfo = _depthInfo.at(_depth); - if (_depth > 0) { - DepthInfo& parentDepthInfo = _depthInfo[_depth - 1]; - parentDepthInfo.minimumColorIncrement = qMin(parentDepthInfo.minimumColorIncrement, depthInfo.minimumColorIncrement); - parentDepthInfo.minimumMaterialIncrement = qMin(parentDepthInfo.minimumMaterialIncrement, - depthInfo.minimumMaterialIncrement); - } - if (_visitations.at(_depth).isInputLeaf(0)) { - HeightfieldBuffer* buffer = NULL; - HeightfieldHeightDataPointer height = info.inputValues.at(0).getInlineValue(); - if (height) { - int heightSize = glm::sqrt((float)height->getContents().size()); - int extendedHeightSize = heightSize + HeightfieldBuffer::HEIGHT_EXTENSION; - int heightContentsSize = extendedHeightSize * extendedHeightSize; - float minimumColorIncrement = depthInfo.minimumColorIncrement; - float minimumMaterialIncrement = depthInfo.minimumMaterialIncrement; - for (int i = _depth - 1; i >= 0 && qMax(minimumColorIncrement, minimumMaterialIncrement) == FLT_MAX; i--) { - const DepthInfo& ancestorDepthInfo = _depthInfo.at(i); - minimumColorIncrement = qMin(minimumColorIncrement, ancestorDepthInfo.minimumColorIncrement); - minimumMaterialIncrement = qMin(minimumMaterialIncrement, ancestorDepthInfo.minimumMaterialIncrement); - } - int colorContentsSize = 0; - if (minimumColorIncrement != FLT_MAX) { - int colorSize = (int)glm::round(info.size / minimumColorIncrement) + HeightfieldBuffer::SHARED_EDGE; - colorContentsSize = colorSize * colorSize * DataBlock::COLOR_BYTES; - } - int materialContentsSize = 0; - if (minimumMaterialIncrement != FLT_MAX) { - int materialSize = (int)glm::round(info.size / minimumMaterialIncrement) + HeightfieldBuffer::SHARED_EDGE; - materialContentsSize = materialSize * materialSize; - } - const HeightfieldBuffer* existingBuffer = static_cast( - info.inputValues.at(3).getInlineValue().data()); - Box bounds = info.getBounds(); - if (existingBuffer && existingBuffer->getHeight().size() == heightContentsSize && - existingBuffer->getColor().size() == colorContentsSize && - existingBuffer->getMaterial().size() == materialContentsSize) { - // we already have a buffer of the correct resolution - addRegion(bounds, existingBuffer->getHeightBounds()); - buffer = new HeightfieldBuffer(info.minimum, info.size, existingBuffer->getHeight(), - existingBuffer->getColor(), existingBuffer->getMaterial(), existingBuffer->getMaterials()); - - } else { - // we must create a new buffer and update its borders - buffer = new HeightfieldBuffer(info.minimum, info.size, QByteArray(heightContentsSize, 0), - QByteArray(colorContentsSize, 0), QByteArray(materialContentsSize, 0)); - const Box& heightBounds = buffer->getHeightBounds(); - addRegion(bounds, heightBounds); - - _intersections.clear(); - _intersections.append(Box(heightBounds.minimum, - glm::vec3(bounds.maximum.x, heightBounds.maximum.y, bounds.minimum.z))); - _intersections.append(Box(glm::vec3(bounds.maximum.x, heightBounds.minimum.y, heightBounds.minimum.z), - glm::vec3(heightBounds.maximum.x, heightBounds.maximum.y, bounds.maximum.z))); - _intersections.append(Box(glm::vec3(bounds.minimum.x, heightBounds.minimum.y, bounds.maximum.z), - heightBounds.maximum)); - _intersections.append(Box(glm::vec3(heightBounds.minimum.x, heightBounds.minimum.y, bounds.minimum.z), - glm::vec3(bounds.minimum.x, heightBounds.maximum.y, heightBounds.maximum.z))); - - _fetchVisitor.init(buffer); - _data->guide(_fetchVisitor); - } - } - BufferDataPointer pointer(buffer); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer)); - } - return true; -} - -void HeightfieldRegionVisitor::addRegion(const Box& unextended, const Box& extended) { - regions.append(unextended); - regionBounds.add(extended); -} - -class HeightfieldUpdateVisitor : public MetavoxelVisitor { -public: - - HeightfieldUpdateVisitor(const MetavoxelLOD& lod, const QVector& regions, const Box& regionBounds); - - virtual int visit(MetavoxelInfo& info); - -private: - - const QVector& _regions; - const Box& _regionBounds; - QVector _intersections; - HeightfieldFetchVisitor _fetchVisitor; -}; - -HeightfieldUpdateVisitor::HeightfieldUpdateVisitor(const MetavoxelLOD& lod, const QVector& regions, - const Box& regionBounds) : - MetavoxelVisitor(QVector() << - Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector() << - Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), lod), - _regions(regions), - _regionBounds(regionBounds), - _fetchVisitor(lod, _intersections) { -} - -int HeightfieldUpdateVisitor::visit(MetavoxelInfo& info) { - if (!info.getBounds().intersects(_regionBounds)) { - return STOP_RECURSION; - } - if (!info.isLeaf) { - return DEFAULT_ORDER; - } - const HeightfieldBuffer* buffer = static_cast( - info.inputValues.at(0).getInlineValue().data()); - if (!buffer) { - return STOP_RECURSION; - } - _intersections.clear(); - foreach (const Box& region, _regions) { - if (region.intersects(buffer->getHeightBounds())) { - _intersections.append(region.getIntersection(buffer->getHeightBounds())); - } - } - if (_intersections.isEmpty()) { - return STOP_RECURSION; - } - HeightfieldBuffer* newBuffer = new HeightfieldBuffer(info.minimum, info.size, - buffer->getHeight(), buffer->getColor(), buffer->getMaterial(), buffer->getMaterials()); - _fetchVisitor.init(newBuffer); - _data->guide(_fetchVisitor); - BufferDataPointer pointer(newBuffer); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer)); - return STOP_RECURSION; -} - class VoxelAugmentVisitor : public MetavoxelVisitor { public: @@ -2162,16 +1262,37 @@ public: glm::vec3 normal; QRgb color; char material; - int axis; }; -class AxisIndex { +const int MAX_NORMALS_PER_VERTEX = 4; + +class NormalIndex { public: - int x, y, z; + int indices[MAX_NORMALS_PER_VERTEX]; - AxisIndex(int x = -1, int y = -1, int z = -1) : x(x), y(y), z(z) { } + int getClosestIndex(const glm::vec3& normal, QVector& vertices) const; }; +int NormalIndex::getClosestIndex(const glm::vec3& normal, QVector& vertices) const { + int firstIndex = indices[0]; + int closestIndex = firstIndex; + const VoxelPoint& firstVertex = vertices.at(firstIndex); + float closest = normal.x * firstVertex.normal[0] + normal.y * firstVertex.normal[1] + normal.z * firstVertex.normal[2]; + for (int i = 1; i < MAX_NORMALS_PER_VERTEX; i++) { + int index = indices[i]; + if (index == firstIndex) { + break; + } + const VoxelPoint& vertex = vertices.at(index); + float product = normal.x * vertex.normal[0] + normal.y * vertex.normal[1] + normal.z * vertex.normal[2]; + if (product > closest) { + closest = product; + closestIndex = index; + } + } + return closestIndex; +} + static glm::vec3 safeNormalize(const glm::vec3& vector) { float length = glm::length(vector); return (length > 0.0f) ? (vector / length) : vector; @@ -2190,7 +1311,7 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { QVector vertices; QVector indices; QVector hermiteSegments; - QMultiHash quadIndices; + QMultiHash quadIndices; // see http://www.frankpetterson.com/publications/dualcontour/dualcontour.pdf for a description of the // dual contour algorithm for generating meshes from voxel data using Hermite-tagged edges @@ -2217,10 +1338,10 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { // as we scan down the cube generating vertices between grid points, we remember the indices of the last // (element, line, section--x, y, z) so that we can connect generated vertices as quads int expanded = size + 1; - QVector lineIndices(expanded); - QVector lastLineIndices(expanded); - QVector planeIndices(expanded * expanded); - QVector lastPlaneIndices(expanded * expanded); + QVector lineIndices(expanded); + QVector lastLineIndices(expanded); + QVector planeIndices(expanded * expanded); + QVector lastPlaneIndices(expanded * expanded); const int EDGES_PER_CUBE = 12; EdgeCrossing crossings[EDGES_PER_CUBE]; @@ -2232,7 +1353,7 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { for (int z = 0; z < expanded; z++) { const QRgb* colorY = colorZ; for (int y = 0; y < expanded; y++) { - AxisIndex lastIndex; + NormalIndex lastIndex; const QRgb* colorX = colorY; for (int x = 0; x < expanded; x++) { int alpha0 = colorX[0] >> ALPHA_OFFSET; @@ -2307,7 +1428,6 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { crossing.material = materialBase ? materialBase[0] : 0; } crossing.point = glm::vec3(qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 0.0f, 0.0f); - crossing.axis = 0; } if (middleY) { if (alpha1 != alpha3) { @@ -2322,7 +1442,6 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { crossing.material = materialBase ? materialBase[1] : 0; } crossing.point = glm::vec3(1.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 0.0f); - crossing.axis = 1; } if (alpha2 != alpha3) { QRgb hermite = hermiteBase[hermiteStride]; @@ -2336,7 +1455,6 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { crossing.material = materialBase ? materialBase[size] : 0; } crossing.point = glm::vec3(qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 1.0f, 0.0f); - crossing.axis = 0; } if (middleZ) { if (alpha3 != alpha7) { @@ -2351,7 +1469,6 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { crossing.material = materialBase ? materialBase[offset3] : 0; } crossing.point = glm::vec3(1.0f, 1.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL); - crossing.axis = 2; } if (alpha5 != alpha7) { QRgb hermite = hermiteBase[hermiteArea + VoxelHermiteData::EDGE_COUNT + 1]; @@ -2365,7 +1482,6 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { crossing.material = materialBase ? materialBase[offset5] : 0; } crossing.point = glm::vec3(1.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 1.0f); - crossing.axis = 1; } if (alpha6 != alpha7) { QRgb hermite = hermiteBase[hermiteArea + hermiteStride]; @@ -2379,7 +1495,6 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { crossing.material = materialBase ? materialBase[offset6] : 0; } crossing.point = glm::vec3(qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 1.0f, 1.0f); - crossing.axis = 0; } } } @@ -2396,7 +1511,6 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { crossing.material = materialBase ? materialBase[1] : 0; } crossing.point = glm::vec3(1.0f, 0.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL); - crossing.axis = 2; } if (alpha4 != alpha5) { QRgb hermite = hermiteBase[hermiteArea]; @@ -2410,7 +1524,6 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { crossing.material = materialBase ? materialBase[area] : 0; } crossing.point = glm::vec3(qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 0.0f, 1.0f); - crossing.axis = 0; } } } @@ -2427,7 +1540,6 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { crossing.material = materialBase ? materialBase[0] : 0; } crossing.point = glm::vec3(0.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 0.0f); - crossing.axis = 1; } if (middleZ) { if (alpha2 != alpha6) { @@ -2442,7 +1554,6 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { crossing.material = materialBase ? materialBase[size] : 0; } crossing.point = glm::vec3(0.0f, 1.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL); - crossing.axis = 2; } if (alpha4 != alpha6) { QRgb hermite = hermiteBase[hermiteArea + 1]; @@ -2456,7 +1567,6 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { crossing.material = materialBase ? materialBase[area] : 0; } crossing.point = glm::vec3(0.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 1.0f); - crossing.axis = 1; } } } @@ -2472,12 +1582,13 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { crossing.material = materialBase ? materialBase[0] : 0; } crossing.point = glm::vec3(0.0f, 0.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL); - crossing.axis = 2; } // at present, we simply average the properties of each crossing as opposed to finding the vertex that // minimizes the quadratic error function as described in the reference paper glm::vec3 center; - glm::vec3 axisNormals[3]; + glm::vec3 normals[MAX_NORMALS_PER_VERTEX]; + int normalCount = 0; + const float CREASE_COS_NORMAL = glm::cos(glm::radians(45.0f)); const int MAX_MATERIALS_PER_VERTEX = 4; quint8 materials[] = { 0, 0, 0, 0 }; glm::vec4 materialWeights; @@ -2486,7 +1597,18 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { for (int i = 0; i < crossingCount; i++) { const EdgeCrossing& crossing = crossings[i]; center += crossing.point; - axisNormals[crossing.axis] += crossing.normal; + + int j = 0; + for (; j < normalCount; j++) { + if (glm::dot(normals[j], crossing.normal) > CREASE_COS_NORMAL) { + normals[j] = safeNormalize(normals[j] + crossing.normal); + break; + } + } + if (j == normalCount) { + normals[normalCount++] = crossing.normal; + } + red += qRed(crossing.color); green += qGreen(crossing.color); blue += qBlue(crossing.color); @@ -2501,7 +1623,7 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { // when assigning a material, search for its presence and, if not found, // place it in the first empty slot if (crossing.material != 0) { - for (int j = 0; j < MAX_MATERIALS_PER_VERTEX; j++) { + for (j = 0; j < MAX_MATERIALS_PER_VERTEX; j++) { if (materials[j] == crossing.material) { materialWeights[j] += 1.0f; totalWeight += 1.0f; @@ -2516,18 +1638,15 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { } } } - glm::vec3 normal = safeNormalize(axisNormals[0] + axisNormals[1] + axisNormals[2]); center /= crossingCount; // use a sequence of Givens rotations to perform a QR decomposition // see http://www.cs.rice.edu/~jwarren/papers/techreport02408.pdf glm::mat4 r(0.0f); glm::vec4 bottom; - float smallestCosNormal = 1.0f; for (int i = 0; i < crossingCount; i++) { const EdgeCrossing& crossing = crossings[i]; bottom = glm::vec4(crossing.normal, glm::dot(crossing.normal, crossing.point - center)); - smallestCosNormal = qMin(smallestCosNormal, glm::dot(crossing.normal, normal)); for (int j = 0; j < 4; j++) { float angle = glm::atan(-bottom[j], r[j][j]); @@ -2592,63 +1711,17 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { } VoxelPoint point = { info.minimum + (glm::vec3(clampedX, clampedY, clampedZ) + center) * scale, { (quint8)(red / crossingCount), (quint8)(green / crossingCount), (quint8)(blue / crossingCount) }, - { (char)(normal.x * 127.0f), (char)(normal.y * 127.0f), (char)(normal.z * 127.0f) }, + { (char)(normals[0].x * 127.0f), (char)(normals[0].y * 127.0f), (char)(normals[0].z * 127.0f) }, { materials[0], materials[1], materials[2], materials[3] }, { (quint8)materialWeights[0], (quint8)materialWeights[1], (quint8)materialWeights[2], (quint8)materialWeights[3] } }; - // determine whether we must "crease" by generating directional normals - const float CREASE_COS_NORMAL = glm::cos(glm::radians(40.0f)); - AxisIndex index(vertices.size(), vertices.size(), vertices.size()); - if (smallestCosNormal > CREASE_COS_NORMAL) { + NormalIndex index = { { vertices.size(), vertices.size(), vertices.size(), vertices.size() } }; + vertices.append(point); + for (int i = 1; i < normalCount; i++) { + index.indices[i] = vertices.size(); + point.setNormal(normals[i]); vertices.append(point); - - } else { - axisNormals[0] = safeNormalize(axisNormals[0]); - axisNormals[1] = safeNormalize(axisNormals[1]); - axisNormals[2] = safeNormalize(axisNormals[2]); - glm::vec3 normalXY(safeNormalize(axisNormals[0] + axisNormals[1])); - glm::vec3 normalXZ(safeNormalize(axisNormals[0] + axisNormals[2])); - glm::vec3 normalYZ(safeNormalize(axisNormals[1] + axisNormals[2])); - if (glm::dot(axisNormals[0], normalXY) > CREASE_COS_NORMAL && - glm::dot(axisNormals[1], normalXY) > CREASE_COS_NORMAL) { - point.setNormal(normalXY); - vertices.append(point); - - point.setNormal(axisNormals[2]); - index.z = vertices.size(); - vertices.append(point); - - } else if (glm::dot(axisNormals[0], normalXZ) > CREASE_COS_NORMAL && - glm::dot(axisNormals[2], normalXZ) > CREASE_COS_NORMAL) { - point.setNormal(normalXZ); - vertices.append(point); - - point.setNormal(axisNormals[1]); - index.y = vertices.size(); - vertices.append(point); - - } else if (glm::dot(axisNormals[1], normalYZ) > CREASE_COS_NORMAL && - glm::dot(axisNormals[2], normalYZ) > CREASE_COS_NORMAL) { - point.setNormal(normalYZ); - vertices.append(point); - - point.setNormal(axisNormals[0]); - index.x = vertices.size(); - vertices.append(point); - - } else { - point.setNormal(axisNormals[0]); - vertices.append(point); - - point.setNormal(axisNormals[1]); - index.y = vertices.size(); - vertices.append(point); - - point.setNormal(axisNormals[2]); - index.z = vertices.size(); - vertices.append(point); - } } // the first x, y, and z are repeated for the boundary edge; past that, we consider generating @@ -2659,19 +1732,25 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { quadIndices.insert(qRgb(x, y - 1, z), indices.size()); quadIndices.insert(qRgb(x, y - 1, z - 1), indices.size()); quadIndices.insert(qRgb(x, y, z - 1), indices.size()); - indices.append(index.x); - int index1 = lastLineIndices.at(x).x; - int index2 = lastPlaneIndices.at((y - 1) * expanded + x).x; - int index3 = lastPlaneIndices.at(y * expanded + x).x; + + const NormalIndex& index1 = lastLineIndices.at(x); + const NormalIndex& index2 = lastPlaneIndices.at((y - 1) * expanded + x); + const NormalIndex& index3 = lastPlaneIndices.at(y * expanded + x); + + const glm::vec3& first = vertices.at(index.indices[0]).vertex; + glm::vec3 normal = glm::cross(vertices.at(index1.indices[0]).vertex - first, + vertices.at(index3.indices[0]).vertex - first); + if (alpha0 == 0) { // quad faces negative x - indices.append(index3); - indices.append(index2); - indices.append(index1); + indices.append(index3.getClosestIndex(normal = -normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index1.getClosestIndex(normal, vertices)); } else { // quad faces positive x - indices.append(index1); - indices.append(index2); - indices.append(index3); + indices.append(index1.getClosestIndex(normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index3.getClosestIndex(normal, vertices)); } + indices.append(index.getClosestIndex(normal, vertices)); } if (alpha0 != alpha2) { @@ -2679,19 +1758,25 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { quadIndices.insert(qRgb(x - 1, y, z), indices.size()); quadIndices.insert(qRgb(x - 1, y, z - 1), indices.size()); quadIndices.insert(qRgb(x, y, z - 1), indices.size()); - indices.append(index.y); - int index1 = lastIndex.y; - int index2 = lastPlaneIndices.at(y * expanded + x - 1).y; - int index3 = lastPlaneIndices.at(y * expanded + x).y; + + const NormalIndex& index1 = lastIndex; + const NormalIndex& index2 = lastPlaneIndices.at(y * expanded + x - 1); + const NormalIndex& index3 = lastPlaneIndices.at(y * expanded + x); + + const glm::vec3& first = vertices.at(index.indices[0]).vertex; + glm::vec3 normal = glm::cross(vertices.at(index3.indices[0]).vertex - first, + vertices.at(index1.indices[0]).vertex - first); + if (alpha0 == 0) { // quad faces negative y - indices.append(index1); - indices.append(index2); - indices.append(index3); + indices.append(index1.getClosestIndex(normal = -normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index3.getClosestIndex(normal, vertices)); } else { // quad faces positive y - indices.append(index3); - indices.append(index2); - indices.append(index1); + indices.append(index3.getClosestIndex(normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index1.getClosestIndex(normal, vertices)); } + indices.append(index.getClosestIndex(normal, vertices)); } if (alpha0 != alpha4) { @@ -2699,19 +1784,25 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { quadIndices.insert(qRgb(x - 1, y, z), indices.size()); quadIndices.insert(qRgb(x - 1, y - 1, z), indices.size()); quadIndices.insert(qRgb(x, y - 1, z), indices.size()); - indices.append(index.z); - int index1 = lastIndex.z; - int index2 = lastLineIndices.at(x - 1).z; - int index3 = lastLineIndices.at(x).z; + + const NormalIndex& index1 = lastIndex; + const NormalIndex& index2 = lastLineIndices.at(x - 1); + const NormalIndex& index3 = lastLineIndices.at(x); + + const glm::vec3& first = vertices.at(index.indices[0]).vertex; + glm::vec3 normal = glm::cross(vertices.at(index1.indices[0]).vertex - first, + vertices.at(index3.indices[0]).vertex - first); + if (alpha0 == 0) { // quad faces negative z - indices.append(index3); - indices.append(index2); - indices.append(index1); + indices.append(index3.getClosestIndex(normal = -normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index1.getClosestIndex(normal, vertices)); } else { // quad faces positive z - indices.append(index1); - indices.append(index2); - indices.append(index3); + indices.append(index1.getClosestIndex(normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index3.getClosestIndex(normal, vertices)); } + indices.append(index.getClosestIndex(normal, vertices)); } } lastIndex = index; @@ -2749,37 +1840,13 @@ void DefaultMetavoxelRendererImplementation::augment(MetavoxelData& data, const while (expandedPrevious.getSize() < data.getSize()) { expandedPrevious.expand(); } - const AttributePointer& pointBufferAttribute = Application::getInstance()->getMetavoxels()->getPointBufferAttribute(); - MetavoxelNode* root = expandedPrevious.getRoot(pointBufferAttribute); - if (root) { - data.setRoot(pointBufferAttribute, root); - root->incrementReferenceCount(); - } - const AttributePointer& heightfieldBufferAttribute = - Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(); - root = expandedPrevious.getRoot(heightfieldBufferAttribute); - if (root) { - data.setRoot(heightfieldBufferAttribute, root); - root->incrementReferenceCount(); - } const AttributePointer& voxelBufferAttribute = Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(); - root = expandedPrevious.getRoot(voxelBufferAttribute); + MetavoxelNode* root = expandedPrevious.getRoot(voxelBufferAttribute); if (root) { data.setRoot(voxelBufferAttribute, root); root->incrementReferenceCount(); } - - PointAugmentVisitor pointAugmentVisitor(lod); - data.guideToDifferent(expandedPrevious, pointAugmentVisitor); - - HeightfieldRegionVisitor heightfieldRegionVisitor(lod); - data.guideToDifferent(expandedPrevious, heightfieldRegionVisitor); - - HeightfieldUpdateVisitor heightfieldUpdateVisitor(lod, heightfieldRegionVisitor.regions, - heightfieldRegionVisitor.regionBounds); - data.guide(heightfieldUpdateVisitor); - VoxelAugmentVisitor voxelAugmentVisitor(lod); data.guideToDifferent(expandedPrevious, voxelAugmentVisitor); } @@ -2789,7 +1856,7 @@ public: SpannerSimulateVisitor(float deltaTime, const MetavoxelLOD& lod); - virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize); + virtual bool visit(Spanner* spanner); private: @@ -2798,11 +1865,11 @@ private: SpannerSimulateVisitor::SpannerSimulateVisitor(float deltaTime, const MetavoxelLOD& lod) : SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute(), - QVector(), QVector(), QVector(), lod), + QVector(), QVector(), lod), _deltaTime(deltaTime) { } -bool SpannerSimulateVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) { +bool SpannerSimulateVisitor::visit(Spanner* spanner) { spanner->getRenderer()->simulate(_deltaTime); return true; } @@ -2819,7 +1886,7 @@ public: SpannerRenderVisitor(const MetavoxelLOD& lod); virtual int visit(MetavoxelInfo& info); - virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize); + virtual bool visit(Spanner* spanner); private: @@ -2828,8 +1895,8 @@ private: SpannerRenderVisitor::SpannerRenderVisitor(const MetavoxelLOD& lod) : SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute(), - QVector(), QVector(), QVector(), - lod, encodeOrder(Application::getInstance()->getDisplayViewFrustum()->getDirection())), + QVector(), QVector(), lod, + encodeOrder(Application::getInstance()->getDisplayViewFrustum()->getDirection())), _containmentDepth(INT_MAX) { } @@ -2845,9 +1912,8 @@ int SpannerRenderVisitor::visit(MetavoxelInfo& info) { return SpannerVisitor::visit(info); } -bool SpannerRenderVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) { - const glm::vec4 OPAQUE_WHITE(1.0f, 1.0f, 1.0f, 1.0f); - spanner->getRenderer()->render(OPAQUE_WHITE, SpannerRenderer::DEFAULT_MODE, clipMinimum, clipSize); +bool SpannerRenderVisitor::visit(Spanner* spanner) { + spanner->getRenderer()->render(); return true; } @@ -2890,175 +1956,24 @@ int BufferRenderVisitor::visit(MetavoxelInfo& info) { } void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod) { - SpannerRenderVisitor spannerRenderVisitor(lod); - data.guide(spannerRenderVisitor); - - int viewport[4]; - glGetIntegerv(GL_VIEWPORT, viewport); - const int VIEWPORT_WIDTH_INDEX = 2; - const int VIEWPORT_HEIGHT_INDEX = 3; - float viewportWidth = viewport[VIEWPORT_WIDTH_INDEX]; - float viewportHeight = viewport[VIEWPORT_HEIGHT_INDEX]; - float viewportDiagonal = sqrtf(viewportWidth * viewportWidth + viewportHeight * viewportHeight); - float worldDiagonal = glm::distance(Application::getInstance()->getDisplayViewFrustum()->getNearBottomLeft(), - Application::getInstance()->getDisplayViewFrustum()->getNearTopRight()); - - _pointProgram.bind(); - _pointProgram.setUniformValue(_pointScaleLocation, viewportDiagonal * - Application::getInstance()->getDisplayViewFrustum()->getNearClip() / worldDiagonal); - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - - glEnable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); - - glDisable(GL_BLEND); - - BufferRenderVisitor pointRenderVisitor(Application::getInstance()->getMetavoxels()->getPointBufferAttribute()); - data.guide(pointRenderVisitor); - - glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); - - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_NORMAL_ARRAY); - - _pointProgram.release(); - - Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, true); - - glEnable(GL_CULL_FACE); - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_EQUAL, 0.0f); - - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - - if (Menu::getInstance()->isOptionChecked(MenuOption::RenderHeightfields)) { - _baseHeightfieldProgram.bind(); - - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - BufferRenderVisitor heightfieldRenderVisitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute()); - data.guide(heightfieldRenderVisitor); - - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - - _baseHeightfieldProgram.release(); + if (Menu::getInstance()->isOptionChecked(MenuOption::RenderSpanners)) { + SpannerRenderVisitor spannerRenderVisitor(lod); + data.guide(spannerRenderVisitor); } - if (Menu::getInstance()->isOptionChecked(MenuOption::RenderDualContourSurfaces)) { - glEnableClientState(GL_COLOR_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - - _baseVoxelProgram.bind(); - BufferRenderVisitor voxelRenderVisitor(Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute()); data.guide(voxelRenderVisitor); - - _baseVoxelProgram.release(); - - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_NORMAL_ARRAY); } - - glDisable(GL_ALPHA_TEST); - glDisable(GL_CULL_FACE); - glEnable(GL_BLEND); - - glDisableClientState(GL_VERTEX_ARRAY); - - Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false); -} - -void DefaultMetavoxelRendererImplementation::loadSplatProgram(const char* type, - ProgramObject& program, SplatLocations& locations) { - program.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + - "shaders/metavoxel_" + type + "_splat.vert"); - program.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + - "shaders/metavoxel_" + type + "_splat.frag"); - program.link(); - - program.bind(); - program.setUniformValue("heightMap", 0); - program.setUniformValue("textureMap", 1); - program.setUniformValueArray("diffuseMaps", SPLAT_TEXTURE_UNITS, SPLAT_COUNT); - locations.heightScale = program.uniformLocation("heightScale"); - locations.textureScale = program.uniformLocation("textureScale"); - locations.splatTextureOffset = program.uniformLocation("splatTextureOffset"); - locations.splatTextureScalesS = program.uniformLocation("splatTextureScalesS"); - locations.splatTextureScalesT = program.uniformLocation("splatTextureScalesT"); - locations.textureValueMinima = program.uniformLocation("textureValueMinima"); - locations.textureValueMaxima = program.uniformLocation("textureValueMaxima"); - locations.materials = program.attributeLocation("materials"); - locations.materialWeights = program.attributeLocation("materialWeights"); - program.release(); -} - -ProgramObject DefaultMetavoxelRendererImplementation::_pointProgram; -int DefaultMetavoxelRendererImplementation::_pointScaleLocation; -ProgramObject DefaultMetavoxelRendererImplementation::_baseHeightfieldProgram; -int DefaultMetavoxelRendererImplementation::_baseHeightScaleLocation; -int DefaultMetavoxelRendererImplementation::_baseColorScaleLocation; -ProgramObject DefaultMetavoxelRendererImplementation::_splatHeightfieldProgram; -DefaultMetavoxelRendererImplementation::SplatLocations DefaultMetavoxelRendererImplementation::_splatHeightfieldLocations; -ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldCursorProgram; -ProgramObject DefaultMetavoxelRendererImplementation::_baseVoxelProgram; -ProgramObject DefaultMetavoxelRendererImplementation::_splatVoxelProgram; -DefaultMetavoxelRendererImplementation::SplatLocations DefaultMetavoxelRendererImplementation::_splatVoxelLocations; -ProgramObject DefaultMetavoxelRendererImplementation::_voxelCursorProgram; - -static void enableClipPlane(GLenum plane, float x, float y, float z, float w) { - GLdouble coefficients[] = { x, y, z, w }; - glClipPlane(plane, coefficients); - glEnable(plane); -} - -void ClippedRenderer::render(const glm::vec4& color, Mode mode, const glm::vec3& clipMinimum, float clipSize) { - if (clipSize == 0.0f) { - renderUnclipped(color, mode); - return; - } - enableClipPlane(GL_CLIP_PLANE0, -1.0f, 0.0f, 0.0f, clipMinimum.x + clipSize); - enableClipPlane(GL_CLIP_PLANE1, 1.0f, 0.0f, 0.0f, -clipMinimum.x); - enableClipPlane(GL_CLIP_PLANE2, 0.0f, -1.0f, 0.0f, clipMinimum.y + clipSize); - enableClipPlane(GL_CLIP_PLANE3, 0.0f, 1.0f, 0.0f, -clipMinimum.y); - enableClipPlane(GL_CLIP_PLANE4, 0.0f, 0.0f, -1.0f, clipMinimum.z + clipSize); - enableClipPlane(GL_CLIP_PLANE5, 0.0f, 0.0f, 1.0f, -clipMinimum.z); - - renderUnclipped(color, mode); - - glDisable(GL_CLIP_PLANE0); - glDisable(GL_CLIP_PLANE1); - glDisable(GL_CLIP_PLANE2); - glDisable(GL_CLIP_PLANE3); - glDisable(GL_CLIP_PLANE4); - glDisable(GL_CLIP_PLANE5); } SphereRenderer::SphereRenderer() { } -void SphereRenderer::render(const glm::vec4& color, Mode mode, const glm::vec3& clipMinimum, float clipSize) { - if (clipSize == 0.0f) { - renderUnclipped(color, mode); - return; - } - // slight performance optimization: don't render if clip bounds are entirely within sphere - Sphere* sphere = static_cast(_spanner); - Box clipBox(clipMinimum, clipMinimum + glm::vec3(clipSize, clipSize, clipSize)); - for (int i = 0; i < Box::VERTEX_COUNT; i++) { - const float CLIP_PROPORTION = 0.95f; - if (glm::distance(sphere->getTranslation(), clipBox.getVertex(i)) >= sphere->getScale() * CLIP_PROPORTION) { - ClippedRenderer::render(color, mode, clipMinimum, clipSize); - return; - } - } -} -void SphereRenderer::renderUnclipped(const glm::vec4& color, Mode mode) { +void SphereRenderer::render(bool cursor) { Sphere* sphere = static_cast(_spanner); - const QColor& ownColor = sphere->getColor(); - glColor4f(ownColor.redF() * color.r, ownColor.greenF() * color.g, ownColor.blueF() * color.b, ownColor.alphaF() * color.a); + const QColor& color = sphere->getColor(); + glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF()); glPushMatrix(); const glm::vec3& translation = sphere->getTranslation(); @@ -3067,7 +1982,7 @@ void SphereRenderer::renderUnclipped(const glm::vec4& color, Mode mode) { glm::vec3 axis = glm::axis(rotation); glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - Application::getInstance()->getGeometryCache()->renderSphere(sphere->getScale(), 10, 10); + Application::getInstance()->getDeferredLightingEffect()->renderSolidSphere(sphere->getScale(), 32, 32); glPopMatrix(); } @@ -3075,10 +1990,10 @@ void SphereRenderer::renderUnclipped(const glm::vec4& color, Mode mode) { CuboidRenderer::CuboidRenderer() { } -void CuboidRenderer::renderUnclipped(const glm::vec4& color, Mode mode) { +void CuboidRenderer::render(bool cursor) { Cuboid* cuboid = static_cast(_spanner); - const QColor& ownColor = cuboid->getColor(); - glColor4f(ownColor.redF() * color.r, ownColor.greenF() * color.g, ownColor.blueF() * color.b, ownColor.alphaF() * color.a); + const QColor& color = cuboid->getColor(); + glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF()); glPushMatrix(); const glm::vec3& translation = cuboid->getTranslation(); @@ -3088,7 +2003,7 @@ void CuboidRenderer::renderUnclipped(const glm::vec4& color, Mode mode) { glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); glScalef(1.0f, cuboid->getAspectY(), cuboid->getAspectZ()); - glutSolidCube(cuboid->getScale() * 2.0f); + Application::getInstance()->getDeferredLightingEffect()->renderSolidCube(cuboid->getScale() * 2.0f); glPopMatrix(); } @@ -3126,25 +2041,11 @@ void StaticModelRenderer::simulate(float deltaTime) { _model->simulate(deltaTime); } -void StaticModelRenderer::renderUnclipped(const glm::vec4& color, Mode mode) { - switch (mode) { - case DIFFUSE_MODE: - _model->render(color.a, Model::DIFFUSE_RENDER_MODE); - break; - - case NORMAL_MODE: - _model->render(color.a, Model::NORMAL_RENDER_MODE); - break; - - default: - _model->render(color.a); - break; - } - _model->render(color.a); +void StaticModelRenderer::render(bool cursor) { + _model->render(); } -bool StaticModelRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - const glm::vec3& clipMinimum, float clipSize, float& distance) const { +bool StaticModelRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { RayIntersectionInfo info; info._rayStart = origin; info._rayDirection = direction; @@ -3170,3 +2071,256 @@ void StaticModelRenderer::applyScale(float scale) { void StaticModelRenderer::applyURL(const QUrl& url) { _model->setURL(url); } + +HeightfieldRenderer::HeightfieldRenderer() { + glGenTextures(1, &_heightTextureID); + glBindTexture(GL_TEXTURE_2D, _heightTextureID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glGenTextures(1, &_colorTextureID); + glBindTexture(GL_TEXTURE_2D, _colorTextureID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glGenTextures(1, &_materialTextureID); + glBindTexture(GL_TEXTURE_2D, _materialTextureID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glBindTexture(GL_TEXTURE_2D, 0); +} + +HeightfieldRenderer::~HeightfieldRenderer() { + glDeleteTextures(1, &_heightTextureID); + glDeleteTextures(1, &_colorTextureID); + glDeleteTextures(1, &_materialTextureID); +} + +void HeightfieldRenderer::init(Spanner* spanner) { + SpannerRenderer::init(spanner); + + Heightfield* heightfield = static_cast(spanner); + applyHeight(heightfield->getHeight()); + applyColor(heightfield->getColor()); + applyMaterial(heightfield->getMaterial()); + + connect(heightfield, &Heightfield::heightChanged, this, &HeightfieldRenderer::applyHeight); + connect(heightfield, &Heightfield::colorChanged, this, &HeightfieldRenderer::applyColor); + connect(heightfield, &Heightfield::materialChanged, this, &HeightfieldRenderer::applyMaterial); +} + +void HeightfieldRenderer::render(bool cursor) { + // create the buffer objects lazily + Heightfield* heightfield = static_cast(_spanner); + if (!heightfield->getHeight()) { + return; + } + int width = heightfield->getHeight()->getWidth(); + int height = heightfield->getHeight()->getContents().size() / width; + + int innerWidth = width - 2 * HeightfieldHeight::HEIGHT_BORDER; + int innerHeight = height - 2 * HeightfieldHeight::HEIGHT_BORDER; + int vertexCount = width * height; + int rows = height - 1; + int columns = width - 1; + int indexCount = rows * columns * 3 * 2; + BufferPair& bufferPair = _bufferPairs[IntPair(width, height)]; + if (!bufferPair.first.isCreated()) { + QVector vertices(vertexCount); + HeightfieldPoint* point = vertices.data(); + + float xStep = 1.0f / (innerWidth - 1); + float zStep = 1.0f / (innerHeight - 1); + float z = -zStep; + float sStep = 1.0f / width; + float tStep = 1.0f / height; + float t = tStep / 2.0f; + for (int i = 0; i < height; i++, z += zStep, t += tStep) { + float x = -xStep; + float s = sStep / 2.0f; + const float SKIRT_LENGTH = 0.25f; + float baseY = (i == 0 || i == height - 1) ? -SKIRT_LENGTH : 0.0f; + for (int j = 0; j < width; j++, point++, x += xStep, s += sStep) { + point->vertex = glm::vec3(x, (j == 0 || j == width - 1) ? -SKIRT_LENGTH : baseY, z); + point->textureCoord = glm::vec2(s, t); + } + } + + bufferPair.first.setUsagePattern(QOpenGLBuffer::StaticDraw); + bufferPair.first.create(); + bufferPair.first.bind(); + bufferPair.first.allocate(vertices.constData(), vertexCount * sizeof(HeightfieldPoint)); + bufferPair.first.release(); + + QVector indices(indexCount); + int* index = indices.data(); + for (int i = 0; i < rows; i++) { + int lineIndex = i * width; + int nextLineIndex = (i + 1) * width; + for (int j = 0; j < columns; j++) { + *index++ = lineIndex + j; + *index++ = nextLineIndex + j; + *index++ = nextLineIndex + j + 1; + + *index++ = nextLineIndex + j + 1; + *index++ = lineIndex + j + 1; + *index++ = lineIndex + j; + } + } + + bufferPair.second = QOpenGLBuffer(QOpenGLBuffer::IndexBuffer); + bufferPair.second.create(); + bufferPair.second.bind(); + bufferPair.second.allocate(indices.constData(), indexCount * sizeof(int)); + bufferPair.second.release(); + } + + float xScale = heightfield->getScale(), zScale = xScale * heightfield->getAspectZ(); + if (cursor) { + bufferPair.first.bind(); + bufferPair.second.bind(); + + glPushMatrix(); + glTranslatef(heightfield->getTranslation().x, heightfield->getTranslation().y, heightfield->getTranslation().z); + glm::vec3 axis = glm::axis(heightfield->getRotation()); + glRotatef(glm::degrees(glm::angle(heightfield->getRotation())), axis.x, axis.y, axis.z); + glScalef(xScale, xScale * heightfield->getAspectY(), zScale); + + HeightfieldPoint* point = 0; + glVertexPointer(3, GL_FLOAT, sizeof(HeightfieldPoint), &point->vertex); + glTexCoordPointer(2, GL_FLOAT, sizeof(HeightfieldPoint), &point->textureCoord); + + glBindTexture(GL_TEXTURE_2D, _heightTextureID); + + glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); + + glBindTexture(GL_TEXTURE_2D, 0); + + glPopMatrix(); + + bufferPair.first.release(); + bufferPair.second.release(); + return; + } + + HeightfieldBaseLayerBatch baseBatch; + baseBatch.vertexBuffer = &bufferPair.first; + baseBatch.indexBuffer = &bufferPair.second; + baseBatch.translation = heightfield->getTranslation(); + baseBatch.rotation = heightfield->getRotation(); + baseBatch.scale = glm::vec3(xScale, xScale * heightfield->getAspectY(), zScale); + baseBatch.vertexCount = vertexCount; + baseBatch.indexCount = indexCount; + baseBatch.heightTextureID = _heightTextureID; + baseBatch.heightScale = glm::vec4(1.0f / width, 1.0f / height, (innerWidth - 1) / -2.0f, (innerHeight - 1) / -2.0f); + baseBatch.colorTextureID = _colorTextureID; + baseBatch.colorScale = glm::vec2((float)width / innerWidth, (float)height / innerHeight); + Application::getInstance()->getMetavoxels()->addHeightfieldBaseBatch(baseBatch); + + if (heightfield->getMaterial() && !_networkTextures.isEmpty()) { + HeightfieldSplatBatch splatBatch; + splatBatch.vertexBuffer = &bufferPair.first; + splatBatch.indexBuffer = &bufferPair.second; + splatBatch.translation = heightfield->getTranslation(); + splatBatch.rotation = heightfield->getRotation(); + splatBatch.scale = glm::vec3(xScale, xScale * heightfield->getAspectY(), zScale); + splatBatch.vertexCount = vertexCount; + splatBatch.indexCount = indexCount; + splatBatch.heightTextureID = _heightTextureID; + splatBatch.heightScale = glm::vec4(1.0f / width, 1.0f / height, 0.0f, 0.0f); + splatBatch.materialTextureID = _materialTextureID; + splatBatch.textureScale = glm::vec2((float)width / innerWidth, (float)height / innerHeight); + splatBatch.splatTextureOffset = glm::vec2( + glm::dot(heightfield->getTranslation(), heightfield->getRotation() * glm::vec3(1.0f, 0.0f, 0.0f)) / xScale, + glm::dot(heightfield->getTranslation(), heightfield->getRotation() * glm::vec3(0.0f, 0.0f, 1.0f)) / zScale); + + const QVector& materials = heightfield->getMaterial()->getMaterials(); + for (int i = 0; i < materials.size(); i += SPLAT_COUNT) { + for (int j = 0; j < SPLAT_COUNT; j++) { + int index = i + j; + if (index < _networkTextures.size()) { + const NetworkTexturePointer& texture = _networkTextures.at(index); + if (texture) { + MaterialObject* material = static_cast(materials.at(index).data()); + splatBatch.splatTextureScalesS[j] = xScale / material->getScaleS(); + splatBatch.splatTextureScalesT[j] = zScale / material->getScaleT(); + splatBatch.splatTextureIDs[j] = texture->getID(); + + } else { + splatBatch.splatTextureIDs[j] = 0; + } + } else { + splatBatch.splatTextureIDs[j] = 0; + } + } + splatBatch.materialIndex = i; + Application::getInstance()->getMetavoxels()->addHeightfieldSplatBatch(splatBatch); + } + } +} + +void HeightfieldRenderer::applyHeight(const HeightfieldHeightPointer& height) { + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glBindTexture(GL_TEXTURE_2D, _heightTextureID); + if (height) { + const QVector& contents = height->getContents(); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R16, height->getWidth(), contents.size() / height->getWidth(), 0, + GL_RED, GL_UNSIGNED_SHORT, contents.constData()); + + } else { + const quint16 ZERO_VALUE = 0; + glTexImage2D(GL_TEXTURE_2D, 0, GL_R16, 1, 1, 0, GL_RED, GL_UNSIGNED_SHORT, &ZERO_VALUE); + } + glBindTexture(GL_TEXTURE_2D, 0); +} + +void HeightfieldRenderer::applyColor(const HeightfieldColorPointer& color) { + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glBindTexture(GL_TEXTURE_2D, _colorTextureID); + if (color) { + const QByteArray& contents = color->getContents(); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, color->getWidth(), + contents.size() / (color->getWidth() * DataBlock::COLOR_BYTES), 0, GL_RGB, GL_UNSIGNED_BYTE, contents.constData()); + + } else { + const quint8 WHITE_COLOR[] = { 255, 255, 255 }; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, WHITE_COLOR); + } + glBindTexture(GL_TEXTURE_2D, 0); +} + +void HeightfieldRenderer::applyMaterial(const HeightfieldMaterialPointer& material) { + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glBindTexture(GL_TEXTURE_2D, _materialTextureID); + if (material) { + const QByteArray& contents = material->getContents(); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, material->getWidth(), contents.size() / material->getWidth(), 0, + GL_RED, GL_UNSIGNED_BYTE, contents.constData()); + + const QVector& materials = material->getMaterials(); + _networkTextures.resize(materials.size()); + for (int i = 0; i < materials.size(); i++) { + const SharedObjectPointer& material = materials.at(i); + if (material) { + _networkTextures[i] = Application::getInstance()->getTextureCache()->getTexture( + static_cast(material.data())->getDiffuse(), SPLAT_TEXTURE); + } else { + _networkTextures[i].clear(); + } + } + } else { + const quint8 ZERO_VALUE = 0; + glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, 1, 1, 0, GL_RED, GL_UNSIGNED_BYTE, &ZERO_VALUE); + _networkTextures.clear(); + } + glBindTexture(GL_TEXTURE_2D, 0); +} + +QHash HeightfieldRenderer::_bufferPairs; + diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 7e9f2ce2ca..c1cdfd3624 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -23,7 +23,11 @@ #include "renderer/ProgramObject.h" +class HeightfieldBaseLayerBatch; +class HeightfieldSplatBatch; class Model; +class VoxelBatch; +class VoxelSplatBatch; /// Renders a metavoxel tree. class MetavoxelSystem : public MetavoxelClientManager { @@ -54,7 +58,6 @@ public: void setNetworkSimulation(const NetworkSimulation& simulation); NetworkSimulation getNetworkSimulation(); - const AttributePointer& getPointBufferAttribute() { return _pointBufferAttribute; } const AttributePointer& getHeightfieldBufferAttribute() { return _heightfieldBufferAttribute; } const AttributePointer& getVoxelBufferAttribute() { return _voxelBufferAttribute; } @@ -65,12 +68,8 @@ public: void renderVoxelCursor(const glm::vec3& position, float radius); - bool findFirstRayHeightfieldIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance); - bool findFirstRayVoxelIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance); - Q_INVOKABLE float getHeightfieldHeight(const glm::vec3& location); - Q_INVOKABLE void paintHeightfieldColor(const glm::vec3& position, float radius, const QColor& color); Q_INVOKABLE void paintHeightfieldMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material); @@ -82,9 +81,13 @@ public: Q_INVOKABLE void setVoxelColor(const SharedObjectPointer& spanner, const QColor& color); Q_INVOKABLE void setVoxelMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material); - - Q_INVOKABLE void deleteTextures(int heightID, int colorID, int textureID); - + + void addHeightfieldBaseBatch(const HeightfieldBaseLayerBatch& batch) { _heightfieldBaseBatches.append(batch); } + void addHeightfieldSplatBatch(const HeightfieldSplatBatch& batch) { _heightfieldSplatBatches.append(batch); } + + void addVoxelBaseBatch(const VoxelBatch& batch) { _voxelBaseBatches.append(batch); } + void addVoxelSplatBatch(const VoxelSplatBatch& batch) { _voxelSplatBatches.append(batch); } + signals: void rendering(); @@ -103,7 +106,6 @@ private: void guideToAugmented(MetavoxelVisitor& visitor, bool render = false); - AttributePointer _pointBufferAttribute; AttributePointer _heightfieldBufferAttribute; AttributePointer _voxelBufferAttribute; @@ -113,6 +115,100 @@ private: NetworkSimulation _networkSimulation; QReadWriteLock _networkSimulationLock; + + QVector _heightfieldBaseBatches; + QVector _heightfieldSplatBatches; + QVector _voxelBaseBatches; + QVector _voxelSplatBatches; + + ProgramObject _baseHeightfieldProgram; + int _baseHeightScaleLocation; + int _baseColorScaleLocation; + + class SplatLocations { + public: + int heightScale; + int textureScale; + int splatTextureOffset; + int splatTextureScalesS; + int splatTextureScalesT; + int textureValueMinima; + int textureValueMaxima; + int materials; + int materialWeights; + }; + + ProgramObject _splatHeightfieldProgram; + SplatLocations _splatHeightfieldLocations; + + int _splatHeightScaleLocation; + int _splatTextureScaleLocation; + int _splatTextureOffsetLocation; + int _splatTextureScalesSLocation; + int _splatTextureScalesTLocation; + int _splatTextureValueMinimaLocation; + int _splatTextureValueMaximaLocation; + + ProgramObject _heightfieldCursorProgram; + + ProgramObject _baseVoxelProgram; + ProgramObject _splatVoxelProgram; + SplatLocations _splatVoxelLocations; + + ProgramObject _voxelCursorProgram; + + static void loadSplatProgram(const char* type, ProgramObject& program, SplatLocations& locations); +}; + +/// Base class for heightfield batches. +class HeightfieldBatch { +public: + QOpenGLBuffer* vertexBuffer; + QOpenGLBuffer* indexBuffer; + glm::vec3 translation; + glm::quat rotation; + glm::vec3 scale; + int vertexCount; + int indexCount; + GLuint heightTextureID; + glm::vec4 heightScale; +}; + +/// A batch containing a heightfield base layer. +class HeightfieldBaseLayerBatch : public HeightfieldBatch { +public: + GLuint colorTextureID; + glm::vec2 colorScale; +}; + +/// A batch containing a heightfield splat. +class HeightfieldSplatBatch : public HeightfieldBatch { +public: + GLuint materialTextureID; + glm::vec2 textureScale; + glm::vec2 splatTextureOffset; + int splatTextureIDs[4]; + glm::vec4 splatTextureScalesS; + glm::vec4 splatTextureScalesT; + int materialIndex; +}; + +/// Base class for voxel batches. +class VoxelBatch { +public: + QOpenGLBuffer* vertexBuffer; + QOpenGLBuffer* indexBuffer; + int vertexCount; + int indexCount; +}; + +/// A batch containing a voxel splat. +class VoxelSplatBatch : public VoxelBatch { +public: + int splatTextureIDs[4]; + glm::vec4 splatTextureScalesS; + glm::vec4 splatTextureScalesT; + int materialIndex; }; /// Generic abstract base class for objects that handle a signal. @@ -124,18 +220,6 @@ public slots: virtual void handle() = 0; }; -/// Describes contents of a point in a point buffer. -class BufferPoint { -public: - glm::vec4 vertex; - quint8 color[3]; - quint8 normal[3]; -}; - -typedef QVector BufferPointVector; - -Q_DECLARE_METATYPE(BufferPointVector) - /// Simple throttle for limiting bandwidth on a per-second basis. class Throttle { public: @@ -203,107 +287,6 @@ public: typedef QExplicitlySharedDataPointer BufferDataPointer; -/// Contains the information necessary to render a group of points. -class PointBuffer : public BufferData { -public: - - PointBuffer(const BufferPointVector& points); - - virtual void render(bool cursor = false); - -private: - - BufferPointVector _points; - QOpenGLBuffer _buffer; - int _pointCount; -}; - -/// Contains the information necessary to render a heightfield block. -class HeightfieldBuffer : public BufferData { -public: - - static const int HEIGHT_BORDER; - static const int SHARED_EDGE; - static const int HEIGHT_EXTENSION; - - HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height, - const QByteArray& color, const QByteArray& material = QByteArray(), - const QVector& materials = QVector()); - ~HeightfieldBuffer(); - - const glm::vec3& getTranslation() const { return _translation; } - float getScale() const { return _scale; } - - const Box& getHeightBounds() const { return _heightBounds; } - const Box& getColorBounds() const { return _colorBounds; } - const Box& getMaterialBounds() const { return _materialBounds; } - - QByteArray& getHeight() { return _height; } - const QByteArray& getHeight() const { return _height; } - - QByteArray& getColor() { return _color; } - const QByteArray& getColor() const { return _color; } - - QByteArray& getMaterial() { return _material; } - const QByteArray& getMaterial() const { return _material; } - - QVector& getMaterials() { return _materials; } - const QVector& getMaterials() const { return _materials; } - - QByteArray getUnextendedHeight() const; - QByteArray getUnextendedColor(int x = 0, int y = 0) const; - - int getHeightSize() const { return _heightSize; } - float getHeightIncrement() const { return _heightIncrement; } - - int getColorSize() const { return _colorSize; } - float getColorIncrement() const { return _colorIncrement; } - - int getMaterialSize() const { return _materialSize; } - float getMaterialIncrement() const { return _materialIncrement; } - - virtual void render(bool cursor = false); - -private: - - glm::vec3 _translation; - float _scale; - Box _heightBounds; - Box _colorBounds; - Box _materialBounds; - QByteArray _height; - QByteArray _color; - QByteArray _material; - QVector _materials; - GLuint _heightTextureID; - GLuint _colorTextureID; - GLuint _materialTextureID; - QVector _networkTextures; - int _heightSize; - float _heightIncrement; - int _colorSize; - float _colorIncrement; - int _materialSize; - float _materialIncrement; - - typedef QPair BufferPair; - static QHash _bufferPairs; -}; - -/// Convenience class for rendering a preview of a heightfield. -class HeightfieldPreview { -public: - - void setBuffers(const QVector& buffers) { _buffers = buffers; } - const QVector& getBuffers() const { return _buffers; } - - void render(const glm::vec3& translation, float scale) const; - -private: - - QVector _buffers; -}; - /// Describes contents of a vertex in a voxel buffer. class VoxelPoint { public: @@ -316,12 +299,27 @@ public: void setNormal(const glm::vec3& normal); }; +/// A container for a coordinate within a voxel block. +class VoxelCoord { +public: + QRgb encoded; + + VoxelCoord(QRgb encoded) : encoded(encoded) { } + + bool operator==(const VoxelCoord& other) const { return encoded == other.encoded; } +}; + +inline uint qHash(const VoxelCoord& coord, uint seed) { + // multiply by prime numbers greater than the possible size + return qHash(qRed(coord.encoded) + 257 * (qGreen(coord.encoded) + 263 * qBlue(coord.encoded)), seed); +} + /// Contains the information necessary to render a voxel block. class VoxelBuffer : public BufferData { public: VoxelBuffer(const QVector& vertices, const QVector& indices, const QVector& hermite, - const QMultiHash& quadIndices, int size, const QVector& materials = + const QMultiHash& quadIndices, int size, const QVector& materials = QVector()); /// Finds the first intersection between the described ray and the voxel data. @@ -336,7 +334,7 @@ private: QVector _vertices; QVector _indices; QVector _hermite; - QMultiHash _quadIndices; + QMultiHash _quadIndices; int _size; int _vertexCount; int _indexCount; @@ -367,120 +365,37 @@ class DefaultMetavoxelRendererImplementation : public MetavoxelRendererImplement public: - static void init(); - - static ProgramObject& getPointProgram() { return _pointProgram; } - static int getPointScaleLocation() { return _pointScaleLocation; } - - static ProgramObject& getBaseHeightfieldProgram() { return _baseHeightfieldProgram; } - static int getBaseHeightScaleLocation() { return _baseHeightScaleLocation; } - static int getBaseColorScaleLocation() { return _baseColorScaleLocation; } - - class SplatLocations { - public: - int heightScale; - int textureScale; - int splatTextureOffset; - int splatTextureScalesS; - int splatTextureScalesT; - int textureValueMinima; - int textureValueMaxima; - int materials; - int materialWeights; - }; - - static ProgramObject& getSplatHeightfieldProgram() { return _splatHeightfieldProgram; } - static const SplatLocations& getSplatHeightfieldLocations() { return _splatHeightfieldLocations; } - - static ProgramObject& getHeightfieldCursorProgram() { return _heightfieldCursorProgram; } - - static ProgramObject& getBaseVoxelProgram() { return _baseVoxelProgram; } - - static ProgramObject& getSplatVoxelProgram() { return _splatVoxelProgram; } - static const SplatLocations& getSplatVoxelLocations() { return _splatVoxelLocations; } - - static ProgramObject& getVoxelCursorProgram() { return _voxelCursorProgram; } - Q_INVOKABLE DefaultMetavoxelRendererImplementation(); virtual void augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod); virtual void simulate(MetavoxelData& data, float deltaTime, MetavoxelInfo& info, const MetavoxelLOD& lod); virtual void render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod); - -private: - - static void loadSplatProgram(const char* type, ProgramObject& program, SplatLocations& locations); - - static ProgramObject _pointProgram; - static int _pointScaleLocation; - - static ProgramObject _baseHeightfieldProgram; - static int _baseHeightScaleLocation; - static int _baseColorScaleLocation; - - static ProgramObject _splatHeightfieldProgram; - static SplatLocations _splatHeightfieldLocations; - - static int _splatHeightScaleLocation; - static int _splatTextureScaleLocation; - static int _splatTextureOffsetLocation; - static int _splatTextureScalesSLocation; - static int _splatTextureScalesTLocation; - static int _splatTextureValueMinimaLocation; - static int _splatTextureValueMaximaLocation; - - static ProgramObject _heightfieldCursorProgram; - - static ProgramObject _baseVoxelProgram; - static ProgramObject _splatVoxelProgram; - static SplatLocations _splatVoxelLocations; - - static ProgramObject _voxelCursorProgram; -}; - -/// Base class for spanner renderers; provides clipping. -class ClippedRenderer : public SpannerRenderer { - Q_OBJECT - -public: - - virtual void render(const glm::vec4& color, Mode mode, const glm::vec3& clipMinimum, float clipSize); - -protected: - - virtual void renderUnclipped(const glm::vec4& color, Mode mode) = 0; }; /// Renders spheres. -class SphereRenderer : public ClippedRenderer { +class SphereRenderer : public SpannerRenderer { Q_OBJECT public: Q_INVOKABLE SphereRenderer(); - virtual void render(const glm::vec4& color, Mode mode, const glm::vec3& clipMinimum, float clipSize); - -protected: - - virtual void renderUnclipped(const glm::vec4& color, Mode mode); + virtual void render(bool cursor = false); }; /// Renders cuboids. -class CuboidRenderer : public ClippedRenderer { +class CuboidRenderer : public SpannerRenderer { Q_OBJECT public: Q_INVOKABLE CuboidRenderer(); -protected: - - virtual void renderUnclipped(const glm::vec4& color, Mode mode); + virtual void render(bool cursor = false); }; /// Renders static models. -class StaticModelRenderer : public ClippedRenderer { +class StaticModelRenderer : public SpannerRenderer { Q_OBJECT public: @@ -489,12 +404,8 @@ public: virtual void init(Spanner* spanner); virtual void simulate(float deltaTime); - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - const glm::vec3& clipMinimum, float clipSize, float& distance) const; - -protected: - - virtual void renderUnclipped(const glm::vec4& color, Mode mode); + virtual void render(bool cursor = false); + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; private slots: @@ -508,4 +419,34 @@ private: Model* _model; }; +/// Renders heightfields. +class HeightfieldRenderer : public SpannerRenderer { + Q_OBJECT + +public: + + Q_INVOKABLE HeightfieldRenderer(); + virtual ~HeightfieldRenderer(); + + virtual void init(Spanner* spanner); + virtual void render(bool cursor = false); + +private slots: + + void applyHeight(const HeightfieldHeightPointer& height); + void applyColor(const HeightfieldColorPointer& color); + void applyMaterial(const HeightfieldMaterialPointer& material); + +private: + + GLuint _heightTextureID; + GLuint _colorTextureID; + GLuint _materialTextureID; + QVector _networkTextures; + + typedef QPair IntPair; + typedef QPair BufferPair; + static QHash _bufferPairs; +}; + #endif // hifi_MetavoxelSystem_h diff --git a/interface/src/Physics.cpp b/interface/src/Physics.cpp index 35751371da..1f8215f9dd 100644 --- a/interface/src/Physics.cpp +++ b/interface/src/Physics.cpp @@ -66,7 +66,7 @@ void ThreadSafePhysicsEngine::initSafe(EntityTree* entities) { void applyStaticFriction(float deltaTime, glm::vec3& velocity, float maxVelocity, float strength) { float v = glm::length(velocity); if (v < maxVelocity) { - velocity *= glm::clamp((1.0f - deltaTime * strength * (1.f - v / maxVelocity)), 0.0f, 1.0f); + velocity *= glm::clamp((1.0f - deltaTime * strength * (1.0f - v / maxVelocity)), 0.0f, 1.0f); } } @@ -75,10 +75,10 @@ void applyStaticFriction(float deltaTime, glm::vec3& velocity, float maxVelocity // void applyDamping(float deltaTime, glm::vec3& velocity, float linearStrength, float squaredStrength) { - if (squaredStrength == 0.f) { - velocity *= glm::clamp(1.f - deltaTime * linearStrength, 0.f, 1.f); + if (squaredStrength == 0.0f) { + velocity *= glm::clamp(1.0f - deltaTime * linearStrength, 0.0f, 1.0f); } else { - velocity *= glm::clamp(1.f - deltaTime * (linearStrength + glm::length(velocity) * squaredStrength), 0.f, 1.f); + velocity *= glm::clamp(1.0f - deltaTime * (linearStrength + glm::length(velocity) * squaredStrength), 0.0f, 1.0f); } } diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index b23b4fecb3..c2e9a2d7e3 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -65,7 +65,7 @@ void renderWorldBox() { glVertex3f(TREE_SCALE, 0, 0); glEnd(); // Draw meter markers along the 3 axis to help with measuring things - const float MARKER_DISTANCE = 1.f; + const float MARKER_DISTANCE = 1.0f; const float MARKER_RADIUS = 0.05f; glEnable(GL_LIGHTING); glPushMatrix(); @@ -93,7 +93,7 @@ void renderWorldBox() { // Return a random vector of average length 1 const glm::vec3 randVector() { - return glm::vec3(randFloat() - 0.5f, randFloat() - 0.5f, randFloat() - 0.5f) * 2.f; + return glm::vec3(randFloat() - 0.5f, randFloat() - 0.5f, randFloat() - 0.5f) * 2.0f; } static TextRenderer* textRenderer(int mono) { @@ -125,7 +125,7 @@ void drawText(int x, int y, float scale, float radians, int mono, glTranslatef(static_cast(x), static_cast(y), 0.0f); glColor3fv(color); glRotated(double(radians * DEGREES_PER_RADIAN), 0.0, 0.0, 1.0); - glScalef(scale / 0.1f, scale / 0.1f, 1.f); + glScalef(scale / 0.1f, scale / 0.1f, 1.0f); textRenderer(mono)->draw(0, 0, string); glPopMatrix(); } diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index df3daba9c8..03243c1a83 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -667,8 +667,9 @@ void Avatar::renderDisplayName() { glTranslatef(textPosition.x, textPosition.y, textPosition.z); // we need "always facing camera": we must remove the camera rotation from the stack + - glm::vec3 frontAxis(0.f, 0.f, 1.f); + glm::vec3 frontAxis(0.0f, 0.0f, 1.0f); if (inHMD) { glm::vec3 camPosition = Application::getInstance()->getCamera()->getPosition(); frontAxis = camPosition - textPosition; @@ -677,8 +678,8 @@ void Avatar::renderDisplayName() { frontAxis = glm::rotate(rotation, frontAxis); } - frontAxis = glm::normalize(glm::vec3(frontAxis.z, 0.f, -frontAxis.x)); - float angle = acos(frontAxis.x) * ((frontAxis.z < 0) ? 1.f : -1.f); + frontAxis = glm::normalize(glm::vec3(frontAxis.z, 0.0f, -frontAxis.x)); + float angle = acos(frontAxis.x) * ((frontAxis.z < 0) ? 1.0f : -1.0f); glRotatef(glm::degrees(angle), 0.0f, 1.0f, 0.0f); // We need to compute the scale factor such as the text remains with fixed size respect to window coordinates diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 376e229d0c..2d1a44403f 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -87,6 +87,7 @@ public: void setDisplayingLookatVectors(bool displayingLookatVectors) { getHead()->setRenderLookatVectors(displayingLookatVectors); } void setMouseRay(const glm::vec3 &origin, const glm::vec3 &direction); void setIsLookAtTarget(const bool isLookAtTarget) { _isLookAtTarget = isLookAtTarget; } + bool getIsLookAtTarget() const { return _isLookAtTarget; } //getters bool isInitialized() const { return _initialized; } SkeletonModel& getSkeletonModel() { return _skeletonModel; } diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index d29e7312ff..148abc3546 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -68,7 +68,7 @@ void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) { skeletonModel.getHandShapes(jointIndex, shapes); if (avatar->findCollisions(shapes, handCollisions)) { - glm::vec3 totalPenetration(0.f); + glm::vec3 totalPenetration(0.0f); glm::vec3 averageContactPoint; for (int j = 0; j < handCollisions.size(); ++j) { CollisionInfo* collision = handCollisions.getCollision(j); @@ -151,7 +151,7 @@ void Hand::renderHandTargets(bool isMine) { const float collisionRadius = 0.05f; glColor4f(0.5f,0.5f,0.5f, alpha); - glutWireSphere(collisionRadius, 10.f, 10.f); + glutWireSphere(collisionRadius, 10.0f, 10.0f); glPopMatrix(); } } diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 7c25bbd6b2..e237d0ae6b 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -45,11 +45,11 @@ Head::Head(Avatar* owningAvatar) : _leftEyeBlinkVelocity(0.0f), _rightEyeBlinkVelocity(0.0f), _timeWithoutTalking(0.0f), - _deltaPitch(0.f), - _deltaYaw(0.f), - _deltaRoll(0.f), - _deltaLeanSideways(0.f), - _deltaLeanForward(0.f), + _deltaPitch(0.0f), + _deltaYaw(0.0f), + _deltaRoll(0.0f), + _deltaLeanSideways(0.0f), + _deltaLeanForward(0.0f), _isCameraMoving(false), _isLookingAtMe(false), _faceModel(this) @@ -86,7 +86,7 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { } // Update audio trailing average for rendering facial animations const float AUDIO_AVERAGING_SECS = 0.05f; - const float AUDIO_LONG_TERM_AVERAGING_SECS = 30.f; + const float AUDIO_LONG_TERM_AVERAGING_SECS = 30.0f; _averageLoudness = glm::mix(_averageLoudness, _audioLoudness, glm::min(deltaTime / AUDIO_AVERAGING_SECS, 1.0f)); if (_longTermAverageLoudness == -1.0) { @@ -225,7 +225,7 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { void Head::relaxLean(float deltaTime) { // restore rotation, lean to neutral positions const float LEAN_RELAXATION_PERIOD = 0.25f; // seconds - float relaxationFactor = 1.f - glm::min(deltaTime / LEAN_RELAXATION_PERIOD, 1.f); + float relaxationFactor = 1.0f - glm::min(deltaTime / LEAN_RELAXATION_PERIOD, 1.0f); _deltaYaw *= relaxationFactor; _deltaPitch *= relaxationFactor; _deltaRoll *= relaxationFactor; @@ -278,7 +278,7 @@ glm::quat Head::getCameraOrientation () const { return getOrientation(); } Avatar* owningAvatar = static_cast(_owningAvatar); - return owningAvatar->getWorldAlignedOrientation() * glm::quat(glm::radians(glm::vec3(_basePitch, 0.f, 0.0f))); + return owningAvatar->getWorldAlignedOrientation() * glm::quat(glm::radians(glm::vec3(_basePitch, 0.0f, 0.0f))); } glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const { @@ -325,13 +325,13 @@ void Head::renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosi glLineWidth(2.0); glBegin(GL_LINES); - glColor4f(0.2f, 0.2f, 0.2f, 1.f); + glColor4f(0.2f, 0.2f, 0.2f, 1.0f); glVertex3f(leftEyePosition.x, leftEyePosition.y, leftEyePosition.z); - glColor4f(1.0f, 1.0f, 1.0f, 0.f); + glColor4f(1.0f, 1.0f, 1.0f, 0.0f); glVertex3f(lookatPosition.x, lookatPosition.y, lookatPosition.z); - glColor4f(0.2f, 0.2f, 0.2f, 1.f); + glColor4f(0.2f, 0.2f, 0.2f, 1.0f); glVertex3f(rightEyePosition.x, rightEyePosition.y, rightEyePosition.z); - glColor4f(1.0f, 1.0f, 1.0f, 0.f); + glColor4f(1.0f, 1.0f, 1.0f, 0.0f); glVertex3f(lookatPosition.x, lookatPosition.y, lookatPosition.z); glEnd(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 8b5fefc3be..3cbe2ac8ae 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -133,8 +133,8 @@ void MyAvatar::reset() { setThrust(glm::vec3(0.0f)); // Reset the pitch and roll components of the avatar's orientation, preserve yaw direction glm::vec3 eulers = safeEulerAngles(getOrientation()); - eulers.x = 0.f; - eulers.z = 0.f; + eulers.x = 0.0f; + eulers.z = 0.0f; setOrientation(glm::quat(eulers)); } @@ -929,15 +929,20 @@ void MyAvatar::updateLookAtTargetAvatar() { } glm::vec3 lookForward = faceRotation * IDENTITY_FRONT; glm::vec3 cameraPosition = Application::getInstance()->getCamera()->getPosition(); - float smallestAngleTo = glm::radians(Application::getInstance()->getCamera()->getFieldOfView()) / 2.f; + + float smallestAngleTo = glm::radians(Application::getInstance()->getCamera()->getFieldOfView()) / 2.0f; + const float KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR = 1.3f; + const float GREATEST_LOOKING_AT_DISTANCE = 10.0f; int howManyLookingAtMe = 0; foreach (const AvatarSharedPointer& avatarPointer, Application::getInstance()->getAvatarManager().getAvatarHash()) { Avatar* avatar = static_cast(avatarPointer.data()); + bool isCurrentTarget = avatar->getIsLookAtTarget(); + float distanceTo = glm::length(avatar->getHead()->getEyePosition() - cameraPosition); avatar->setIsLookAtTarget(false); - if (!avatar->isMyAvatar() && avatar->isInitialized()) { + if (!avatar->isMyAvatar() && avatar->isInitialized() && (distanceTo < GREATEST_LOOKING_AT_DISTANCE * getScale())) { float angleTo = glm::angle(lookForward, glm::normalize(avatar->getHead()->getEyePosition() - cameraPosition)); - if (angleTo < smallestAngleTo) { + if (angleTo < (smallestAngleTo * (isCurrentTarget ? KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR : 1.0f))) { _lookAtTargetAvatar = avatarPointer; _targetAvatarPosition = avatarPointer->getPosition(); smallestAngleTo = angleTo; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 0060c959e4..dd1178c7b5 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -101,7 +101,7 @@ public: // Set what driving keys are being pressed to control thrust levels void clearDriveKeys(); void setDriveKeys(int key, float val) { _driveKeys[key] = val; }; - bool getDriveKeys(int key) { return _driveKeys[key] != 0.f; }; + bool getDriveKeys(int key) { return _driveKeys[key] != 0.0f; }; void jump() { _shouldJump = true; }; bool isMyAvatar() { return true; } diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 834f1c2c08..911ebb7412 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -34,7 +34,7 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) : _boundingShape(), _boundingShapeLocalOffset(0.0f), _ragdoll(NULL), - _defaultEyeModelPosition(glm::vec3(0.f, 0.f, 0.f)), + _defaultEyeModelPosition(glm::vec3(0.0f, 0.0f, 0.0f)), _standingFoot(NO_FOOT), _standingOffset(0.0f), _clampedFootPosition(0.0f) { @@ -54,7 +54,7 @@ void SkeletonModel::setJointStates(QVector states) { glm::vec3 leftEyePosition, rightEyePosition; getEyeModelPositions(leftEyePosition, rightEyePosition); - glm::vec3 midEyePosition = (leftEyePosition + rightEyePosition) / 2.f; + glm::vec3 midEyePosition = (leftEyePosition + rightEyePosition) / 2.0f; int rootJointIndex = _geometry->getFBXGeometry().rootJointIndex; glm::vec3 rootModelPosition; @@ -618,6 +618,9 @@ void SkeletonModel::updateVisibleJointStates() { /// \return offset of hips after foot animation void SkeletonModel::updateStandingFoot() { + if (_geometry == NULL) { + return; + } glm::vec3 offset(0.0f); int leftFootIndex = _geometry->getFBXGeometry().leftToeJointIndex; int rightFootIndex = _geometry->getFBXGeometry().rightToeJointIndex; diff --git a/interface/src/devices/DdeFaceTracker.cpp b/interface/src/devices/DdeFaceTracker.cpp index b9f7e338ca..d8766ca0ab 100644 --- a/interface/src/devices/DdeFaceTracker.cpp +++ b/interface/src/devices/DdeFaceTracker.cpp @@ -191,7 +191,7 @@ void resetCoefficient(float * coefficient, float currentValue) { float updateAndGetCoefficient(float * coefficient, float currentValue, bool scaleToRange = false) { coefficient[MIN] = (currentValue < coefficient[MIN]) ? currentValue : coefficient[MIN]; coefficient[MAX] = (currentValue > coefficient[MAX]) ? currentValue : coefficient[MAX]; - coefficient[AVG] = LONG_TERM_AVERAGE * coefficient[AVG] + (1.f - LONG_TERM_AVERAGE) * currentValue; + coefficient[AVG] = LONG_TERM_AVERAGE * coefficient[AVG] + (1.0f - LONG_TERM_AVERAGE) * currentValue; if (coefficient[MAX] > coefficient[MIN]) { if (scaleToRange) { return glm::clamp((currentValue - coefficient[AVG]) / (coefficient[MAX] - coefficient[MIN]), 0.0f, 1.0f); diff --git a/interface/src/devices/Leapmotion.cpp b/interface/src/devices/Leapmotion.cpp index 0c1433f216..62037ae7f6 100644 --- a/interface/src/devices/Leapmotion.cpp +++ b/interface/src/devices/Leapmotion.cpp @@ -156,7 +156,7 @@ void Leapmotion::update() { if (lastFrameID >= newFrameID) return; - glm::vec3 delta(0.f); + glm::vec3 delta(0.0f); glm::quat handOri; if (!frame.hands().isEmpty()) { for (int handNum = 0; handNum < frame.hands().count(); handNum++) { diff --git a/interface/src/devices/MotionTracker.cpp b/interface/src/devices/MotionTracker.cpp index a7fa87d403..6b81c94f82 100644 --- a/interface/src/devices/MotionTracker.cpp +++ b/interface/src/devices/MotionTracker.cpp @@ -167,9 +167,9 @@ MotionTracker::Frame::Frame() : void MotionTracker::Frame::setRotation(const glm::quat& rotation) { glm::mat3x3 rot = glm::mat3_cast(rotation); - _transform[0] = glm::vec4(rot[0], 0.f); - _transform[1] = glm::vec4(rot[1], 0.f); - _transform[2] = glm::vec4(rot[2], 0.f); + _transform[0] = glm::vec4(rot[0], 0.0f); + _transform[1] = glm::vec4(rot[1], 0.0f); + _transform[2] = glm::vec4(rot[2], 0.0f); } void MotionTracker::Frame::getRotation(glm::quat& rotation) const { @@ -177,7 +177,7 @@ void MotionTracker::Frame::getRotation(glm::quat& rotation) const { } void MotionTracker::Frame::setTranslation(const glm::vec3& translation) { - _transform[3] = glm::vec4(translation, 1.f); + _transform[3] = glm::vec4(translation, 1.0f); } void MotionTracker::Frame::getTranslation(glm::vec3& translation) const { diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 8d6d11dd68..e68a08a8f4 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -56,7 +56,7 @@ Camera* OculusManager::_camera = NULL; int OculusManager::_activeEyeIndex = -1; float OculusManager::CALIBRATION_DELTA_MINIMUM_LENGTH = 0.02f; -float OculusManager::CALIBRATION_DELTA_MINIMUM_ANGLE = 5.f * RADIANS_PER_DEGREE; +float OculusManager::CALIBRATION_DELTA_MINIMUM_ANGLE = 5.0f * RADIANS_PER_DEGREE; float OculusManager::CALIBRATION_ZERO_MAXIMUM_LENGTH = 0.01f; float OculusManager::CALIBRATION_ZERO_MAXIMUM_ANGLE = 2.0f * RADIANS_PER_DEGREE; quint64 OculusManager::CALIBRATION_ZERO_HOLD_TIME = 3000000; // usec @@ -196,7 +196,7 @@ void OculusManager::positionCalibrationBillboard(Text3DOverlay* billboard) { headOrientation.z = 0; glm::normalize(headOrientation); billboard->setPosition(Application::getInstance()->getAvatar()->getHeadPosition() - + headOrientation * glm::vec3(0.f, 0.f, -CALIBRATION_MESSAGE_DISTANCE)); + + headOrientation * glm::vec3(0.0f, 0.0f, -CALIBRATION_MESSAGE_DISTANCE)); billboard->setRotation(headOrientation); } #endif diff --git a/interface/src/devices/PrioVR.cpp b/interface/src/devices/PrioVR.cpp index e810f9e370..d3e985d259 100644 --- a/interface/src/devices/PrioVR.cpp +++ b/interface/src/devices/PrioVR.cpp @@ -106,7 +106,7 @@ static void setPalm(float deltaTime, int index) { // Compute current velocity from position change glm::vec3 rawVelocity; - if (deltaTime > 0.f) { + if (deltaTime > 0.0f) { rawVelocity = (position - palm->getRawPosition()) / deltaTime; } else { rawVelocity = glm::vec3(0.0f); @@ -119,10 +119,10 @@ static void setPalm(float deltaTime, int index) { const glm::vec3 FINGER_VECTOR(0.0f, 0.0f, FINGER_LENGTH); const glm::vec3 newTipPosition = position + rotation * FINGER_VECTOR; glm::vec3 oldTipPosition = palm->getTipRawPosition(); - if (deltaTime > 0.f) { + if (deltaTime > 0.0f) { palm->setTipVelocity((newTipPosition - oldTipPosition) / deltaTime); } else { - palm->setTipVelocity(glm::vec3(0.f)); + palm->setTipVelocity(glm::vec3(0.0f)); } palm->setTipPosition(newTipPosition); } diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 48ea85214a..d636f8929c 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -241,16 +241,16 @@ void SixenseManager::update(float deltaTime) { // Transform the measured position into body frame. glm::vec3 neck = _neckBase; // Zeroing y component of the "neck" effectively raises the measured position a little bit. - neck.y = 0.f; + neck.y = 0.0f; position = _orbRotation * (position - neck); // Rotation of Palm glm::quat rotation(data->rot_quat[3], -data->rot_quat[0], data->rot_quat[1], -data->rot_quat[2]); - rotation = glm::angleAxis(PI, glm::vec3(0.f, 1.f, 0.f)) * _orbRotation * rotation; + rotation = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)) * _orbRotation * rotation; // Compute current velocity from position change glm::vec3 rawVelocity; - if (deltaTime > 0.f) { + if (deltaTime > 0.0f) { rawVelocity = (position - palm->getRawPosition()) / deltaTime; } else { rawVelocity = glm::vec3(0.0f); @@ -287,10 +287,10 @@ void SixenseManager::update(float deltaTime) { const glm::vec3 FINGER_VECTOR(0.0f, 0.0f, FINGER_LENGTH); const glm::vec3 newTipPosition = position + rotation * FINGER_VECTOR; glm::vec3 oldTipPosition = palm->getTipRawPosition(); - if (deltaTime > 0.f) { + if (deltaTime > 0.0f) { palm->setTipVelocity((newTipPosition - oldTipPosition) / deltaTime); } else { - palm->setTipVelocity(glm::vec3(0.f)); + palm->setTipVelocity(glm::vec3(0.0f)); } palm->setTipPosition(newTipPosition); } @@ -348,7 +348,7 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers) // to also handle the case where left and right controllers have been reversed. _neckBase = 0.5f * (_reachLeft + _reachRight); // neck is midway between right and left reaches glm::vec3 xAxis = glm::normalize(_reachRight - _reachLeft); - glm::vec3 yAxis(0.f, 1.f, 0.f); + glm::vec3 yAxis(0.0f, 1.0f, 0.0f); glm::vec3 zAxis = glm::normalize(glm::cross(xAxis, yAxis)); xAxis = glm::normalize(glm::cross(yAxis, zAxis)); _orbRotation = glm::inverse(glm::quat_cast(glm::mat3(xAxis, yAxis, zAxis))); @@ -405,7 +405,7 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers) } else if (now > _lockExpiry) { // lock has expired so clamp the data and move on _lockExpiry = now + LOCK_DURATION; - _lastDistance = 0.f; + _lastDistance = 0.0f; _reachUp = 0.5f * (_reachLeft + _reachRight); _calibrationState = CALIBRATION_STATE_Y; qDebug("success: sixense calibration: left"); @@ -424,7 +424,7 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers) if (_lastDistance > MINIMUM_ARM_REACH) { // lock has expired so clamp the data and move on _reachForward = _reachUp; - _lastDistance = 0.f; + _lastDistance = 0.0f; _lockExpiry = now + LOCK_DURATION; _calibrationState = CALIBRATION_STATE_Z; qDebug("success: sixense calibration: up"); @@ -435,7 +435,7 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers) glm::vec3 xAxis = glm::normalize(_reachRight - _reachLeft); glm::vec3 torso = 0.5f * (_reachLeft + _reachRight); //glm::vec3 yAxis = glm::normalize(_reachUp - torso); - glm::vec3 yAxis(0.f, 1.f, 0.f); + glm::vec3 yAxis(0.0f, 1.0f, 0.0f); glm::vec3 zAxis = glm::normalize(glm::cross(xAxis, yAxis)); glm::vec3 averagePosition = 0.5f * (_averageLeft + _averageRight); diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp index 016b7b5363..192b42c58b 100644 --- a/interface/src/entities/EntityTreeRenderer.cpp +++ b/interface/src/entities/EntityTreeRenderer.cpp @@ -64,6 +64,9 @@ EntityTreeRenderer::~EntityTreeRenderer() { } void EntityTreeRenderer::clear() { + foreach (const EntityItemID& entityID, _entityScripts.keys()) { + checkAndCallUnload(entityID); + } OctreeRenderer::clear(); _entityScripts.clear(); } @@ -82,11 +85,11 @@ void EntityTreeRenderer::init() { // make sure our "last avatar position" is something other than our current position, so that on our // first chance, we'll check for enter/leave entity events. glm::vec3 avatarPosition = Application::getInstance()->getAvatar()->getPosition(); - _lastAvatarPosition = avatarPosition + glm::vec3(1.f, 1.f, 1.f); + _lastAvatarPosition = avatarPosition + glm::vec3(1.0f, 1.0f, 1.0f); connect(entityTree, &EntityTree::deletingEntity, this, &EntityTreeRenderer::deletingEntity); connect(entityTree, &EntityTree::addingEntity, this, &EntityTreeRenderer::checkAndCallPreload); - connect(entityTree, &EntityTree::entityScriptChanging, this, &EntityTreeRenderer::checkAndCallPreload); + connect(entityTree, &EntityTree::entityScriptChanging, this, &EntityTreeRenderer::entitySciptChanging); connect(entityTree, &EntityTree::changingEntityID, this, &EntityTreeRenderer::changingEntityID); } @@ -192,6 +195,22 @@ QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) { return entityScriptObject; // newly constructed } +QScriptValue EntityTreeRenderer::getPreviouslyLoadedEntityScript(const EntityItemID& entityItemID) { + EntityItem* entity = static_cast(_tree)->findEntityByEntityItemID(entityItemID); + return getPreviouslyLoadedEntityScript(entity); +} + + +QScriptValue EntityTreeRenderer::getPreviouslyLoadedEntityScript(EntityItem* entity) { + if (entity) { + EntityItemID entityID = entity->getEntityItemID(); + if (_entityScripts.contains(entityID)) { + EntityScriptDetails details = _entityScripts[entityID]; + return details.scriptObject; // previously loaded + } + } + return QScriptValue(); // no script +} void EntityTreeRenderer::setTree(Octree* newTree) { OctreeRenderer::setTree(newTree); static_cast(_tree)->setFBXService(this); @@ -272,7 +291,7 @@ void EntityTreeRenderer::checkEnterLeaveEntities() { } void EntityTreeRenderer::render(RenderArgs::RenderMode renderMode, RenderArgs::RenderSide renderSide) { - bool dontRenderAsScene = !Menu::getInstance()->isOptionChecked(MenuOption::RenderEntitiesAsScene); + bool dontRenderAsScene = Menu::getInstance()->isOptionChecked(MenuOption::DontRenderEntitiesAsScene); if (dontRenderAsScene) { OctreeRenderer::render(renderMode, renderSide); @@ -842,9 +861,15 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI } void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) { + checkAndCallUnload(entityID); _entityScripts.remove(entityID); } +void EntityTreeRenderer::entitySciptChanging(const EntityItemID& entityID) { + checkAndCallUnload(entityID); + checkAndCallPreload(entityID); +} + void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID) { // load the entity script if needed... QScriptValue entityScript = loadEntityScript(entityID); @@ -854,6 +879,15 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID) { } } +void EntityTreeRenderer::checkAndCallUnload(const EntityItemID& entityID) { + QScriptValue entityScript = getPreviouslyLoadedEntityScript(entityID); + if (entityScript.property("unload").isValid()) { + QScriptValueList entityArgs = createEntityArgs(entityID); + entityScript.property("unload").call(entityScript, entityArgs); + } +} + + void EntityTreeRenderer::changingEntityID(const EntityItemID& oldEntityID, const EntityItemID& newEntityID) { if (_entityScripts.contains(oldEntityID)) { EntityScriptDetails details = _entityScripts[oldEntityID]; diff --git a/interface/src/entities/EntityTreeRenderer.h b/interface/src/entities/EntityTreeRenderer.h index 7a8155cd6b..e5eba79e0d 100644 --- a/interface/src/entities/EntityTreeRenderer.h +++ b/interface/src/entities/EntityTreeRenderer.h @@ -106,9 +106,12 @@ signals: public slots: void deletingEntity(const EntityItemID& entityID); void changingEntityID(const EntityItemID& oldEntityID, const EntityItemID& newEntityID); - void checkAndCallPreload(const EntityItemID& entityID); + void entitySciptChanging(const EntityItemID& entityID); private: + void checkAndCallPreload(const EntityItemID& entityID); + void checkAndCallUnload(const EntityItemID& entityID); + QList _releasedModels; void renderProxies(const EntityItem* entity, RenderArgs* args); PickRay computePickRay(float x, float y); @@ -127,6 +130,8 @@ private: QScriptValue loadEntityScript(EntityItem* entity); QScriptValue loadEntityScript(const EntityItemID& entityItemID); + QScriptValue getPreviouslyLoadedEntityScript(const EntityItemID& entityItemID); + QScriptValue getPreviouslyLoadedEntityScript(EntityItem* entity); QString loadScriptContents(const QString& scriptMaybeURLorText); QScriptValueList createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID); QScriptValueList createMouseEventArgs(const EntityItemID& entityID, const MouseEvent& mouseEvent); diff --git a/interface/src/entities/RenderableModelEntityItem.cpp b/interface/src/entities/RenderableModelEntityItem.cpp index 7b2856bb18..10b18ad9c5 100644 --- a/interface/src/entities/RenderableModelEntityItem.cpp +++ b/interface/src/entities/RenderableModelEntityItem.cpp @@ -172,7 +172,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) { // TODO: this is the majority of model render time. And rendering of a cube model vs the basic Box render // is significantly more expensive. Is there a way to call this that doesn't cost us as much? PerformanceTimer perfTimer("model->render"); - bool dontRenderAsScene = !Menu::getInstance()->isOptionChecked(MenuOption::RenderEntitiesAsScene); + bool dontRenderAsScene = Menu::getInstance()->isOptionChecked(MenuOption::DontRenderEntitiesAsScene); if (dontRenderAsScene) { _model->render(alpha, modelRenderMode, args); } else { diff --git a/interface/src/renderer/DeferredLightingEffect.cpp b/interface/src/renderer/DeferredLightingEffect.cpp index 917a6de00e..be4e457131 100644 --- a/interface/src/renderer/DeferredLightingEffect.cpp +++ b/interface/src/renderer/DeferredLightingEffect.cpp @@ -248,9 +248,9 @@ void DeferredLightingEffect::render() { glLightfv(GL_LIGHT1, GL_DIFFUSE, (const GLfloat*)&light.diffuse); glLightfv(GL_LIGHT1, GL_SPECULAR, (const GLfloat*)&light.specular); glLightfv(GL_LIGHT1, GL_POSITION, (const GLfloat*)&light.position); - glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, (light.constantAttenuation > 0.f ? light.constantAttenuation : 0.f)); - glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, (light.linearAttenuation > 0.f ? light.linearAttenuation : 0.f)); - glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, (light.quadraticAttenuation > 0.f ? light.quadraticAttenuation : 0.f)); + glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, (light.constantAttenuation > 0.0f ? light.constantAttenuation : 0.0f)); + glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, (light.linearAttenuation > 0.0f ? light.linearAttenuation : 0.0f)); + glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, (light.quadraticAttenuation > 0.0f ? light.quadraticAttenuation : 0.0f)); glPushMatrix(); @@ -293,9 +293,9 @@ void DeferredLightingEffect::render() { glLightfv(GL_LIGHT1, GL_DIFFUSE, (const GLfloat*)&light.diffuse); glLightfv(GL_LIGHT1, GL_SPECULAR, (const GLfloat*)&light.specular); glLightfv(GL_LIGHT1, GL_POSITION, (const GLfloat*)&light.position); - glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, (light.constantAttenuation > 0.f ? light.constantAttenuation : 0.f)); - glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, (light.linearAttenuation > 0.f ? light.linearAttenuation : 0.f)); - glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, (light.quadraticAttenuation > 0.f ? light.quadraticAttenuation : 0.f)); + glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, (light.constantAttenuation > 0.0f ? light.constantAttenuation : 0.0f)); + glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, (light.linearAttenuation > 0.0f ? light.linearAttenuation : 0.0f)); + glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, (light.quadraticAttenuation > 0.0f ? light.quadraticAttenuation : 0.0f)); glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, (const GLfloat*)&light.direction); glLightf(GL_LIGHT1, GL_SPOT_EXPONENT, light.exponent); glLightf(GL_LIGHT1, GL_SPOT_CUTOFF, glm::degrees(light.cutoff)); diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index 9052ec910f..4e5330cde6 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -296,7 +296,7 @@ void GeometryCache::renderHalfCylinder(int slices, int stacks) { float y = (float)i / (stacks - 1); for (int j = 0; j <= slices; j++) { - float theta = 3.f * PI_OVER_TWO + PI * (float)j / (float)slices; + float theta = 3.0f * PI_OVER_TWO + PI * (float)j / (float)slices; //normals *(vertex++) = sinf(theta); diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index fc3845d94f..32da823be2 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -1330,7 +1330,7 @@ void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm: centerOfMass += _jointStates[massIndex].getPosition() - pivot; } // the gravitational effect is a rotation that tends to align the two cross products - const glm::vec3 worldAlignment = glm::vec3(0.0f, -1.f, 0.0f); + const glm::vec3 worldAlignment = glm::vec3(0.0f, -1.0f, 0.0f); glm::quat gravityDelta = rotationBetween(glm::cross(centerOfMass, leverArm), glm::cross(worldAlignment, leverArm)); @@ -2173,7 +2173,7 @@ int Model::renderMeshesFromList(QVector& list, gpu::Batch& batch, RenderMod GLBATCH(glMaterialfv)(GL_FRONT, GL_AMBIENT, (const float*)&diffuse); GLBATCH(glMaterialfv)(GL_FRONT, GL_DIFFUSE, (const float*)&diffuse); GLBATCH(glMaterialfv)(GL_FRONT, GL_SPECULAR, (const float*)&specular); - GLBATCH(glMaterialf)(GL_FRONT, GL_SHININESS, (part.shininess > 128.f ? 128.f: part.shininess)); + GLBATCH(glMaterialf)(GL_FRONT, GL_SHININESS, (part.shininess > 128.0f ? 128.0f: part.shininess)); Texture* diffuseMap = networkPart.diffuseTexture.data(); if (mesh.isEye && diffuseMap) { diff --git a/interface/src/renderer/ProgramObject.cpp b/interface/src/renderer/ProgramObject.cpp index 16b3461ad0..ff2b1cc11e 100644 --- a/interface/src/renderer/ProgramObject.cpp +++ b/interface/src/renderer/ProgramObject.cpp @@ -15,6 +15,14 @@ ProgramObject::ProgramObject(QObject* parent) : QGLShaderProgram(parent) { } +void ProgramObject::setUniform(int location, const glm::vec2& value) { + setUniformValue(location, value.x, value.y); +} + +void ProgramObject::setUniform(const char* name, const glm::vec2& value) { + setUniformValue(name, value.x, value.y); +} + void ProgramObject::setUniform(int location, const glm::vec3& value) { setUniformValue(location, value.x, value.y, value.z); } @@ -23,6 +31,14 @@ void ProgramObject::setUniform(const char* name, const glm::vec3& value) { setUniformValue(name, value.x, value.y, value.z); } +void ProgramObject::setUniform(int location, const glm::vec4& value) { + setUniformValue(location, value.x, value.y, value.z, value.w); +} + +void ProgramObject::setUniform(const char* name, const glm::vec4& value) { + setUniformValue(name, value.x, value.y, value.z, value.w); +} + void ProgramObject::setUniformArray(const char* name, const glm::vec3* values, int count) { GLfloat* floatVal = new GLfloat[count*3]; int index = 0; diff --git a/interface/src/renderer/ProgramObject.h b/interface/src/renderer/ProgramObject.h index 8e66ce9bc9..5014c6d32b 100644 --- a/interface/src/renderer/ProgramObject.h +++ b/interface/src/renderer/ProgramObject.h @@ -21,8 +21,12 @@ public: ProgramObject(QObject* parent = 0); + void setUniform(int location, const glm::vec2& value); + void setUniform(const char* name, const glm::vec2& value); void setUniform(int location, const glm::vec3& value); void setUniform(const char* name, const glm::vec3& value); + void setUniform(int location, const glm::vec4& value); + void setUniform(const char* name, const glm::vec4& value); void setUniformArray(const char* name, const glm::vec3* values, int count); }; diff --git a/interface/src/starfield/Generator.cpp b/interface/src/starfield/Generator.cpp index d9773e4452..f20521bfbf 100644 --- a/interface/src/starfield/Generator.cpp +++ b/interface/src/starfield/Generator.cpp @@ -16,7 +16,7 @@ using namespace starfield; const float Generator::STAR_COLORIZATION = 0.1f; -const float PI_OVER_180 = 3.14159265358979f / 180.f; +const float PI_OVER_180 = 3.14159265358979f / 180.0f; void Generator::computeStarPositions(InputVertices& destination, unsigned limit, unsigned seed) { InputVertices* vertices = & destination; @@ -44,9 +44,9 @@ void Generator::computeStarPositions(InputVertices& destination, unsigned limit, for(int star = 0; star < ceil(limit * MILKY_WAY_RATIO); ++star) { float azimuth = ((float)rand() / (float) RAND_MAX) * NUM_DEGREES; - float altitude = powf(randFloat()*0.5f, 2.f)/0.25f * MILKY_WAY_WIDTH; + float altitude = powf(randFloat()*0.5f, 2.0f)/0.25f * MILKY_WAY_WIDTH; if (randFloat() > 0.5f) { - altitude *= -1.f; + altitude *= -1.0f; } // we need to rotate the Milky Way band to the correct orientation in the sky diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index e0aa1f3020..55d4cf7c8b 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -999,26 +999,26 @@ void ApplicationOverlay::renderAudioMeter() { const float AUDIO_RED_START = 0.80 * AUDIO_METER_SCALE_WIDTH; const float CLIPPING_INDICATOR_TIME = 1.0f; const float AUDIO_METER_AVERAGING = 0.5; - const float LOG2 = log(2.f); - const float METER_LOUDNESS_SCALE = 2.8f / 5.f; - const float LOG2_LOUDNESS_FLOOR = 11.f; - float audioLevel = 0.f; - float loudness = audio->getLastInputLoudness() + 1.f; + const float LOG2 = log(2.0f); + const float METER_LOUDNESS_SCALE = 2.8f / 5.0f; + const float LOG2_LOUDNESS_FLOOR = 11.0f; + float audioLevel = 0.0f; + float loudness = audio->getLastInputLoudness() + 1.0f; - _trailingAudioLoudness = AUDIO_METER_AVERAGING * _trailingAudioLoudness + (1.f - AUDIO_METER_AVERAGING) * loudness; + _trailingAudioLoudness = AUDIO_METER_AVERAGING * _trailingAudioLoudness + (1.0f - AUDIO_METER_AVERAGING) * loudness; float log2loudness = log(_trailingAudioLoudness) / LOG2; if (log2loudness <= LOG2_LOUDNESS_FLOOR) { audioLevel = (log2loudness / LOG2_LOUDNESS_FLOOR) * METER_LOUDNESS_SCALE * AUDIO_METER_SCALE_WIDTH; } else { - audioLevel = (log2loudness - (LOG2_LOUDNESS_FLOOR - 1.f)) * METER_LOUDNESS_SCALE * AUDIO_METER_SCALE_WIDTH; + audioLevel = (log2loudness - (LOG2_LOUDNESS_FLOOR - 1.0f)) * METER_LOUDNESS_SCALE * AUDIO_METER_SCALE_WIDTH; } if (audioLevel > AUDIO_METER_SCALE_WIDTH) { audioLevel = AUDIO_METER_SCALE_WIDTH; } - bool isClipping = ((audio->getTimeSinceLastClip() > 0.f) && (audio->getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME)); + bool isClipping = ((audio->getTimeSinceLastClip() > 0.0f) && (audio->getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME)); - if ((audio->getTimeSinceLastClip() > 0.f) && (audio->getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME)) { + if ((audio->getTimeSinceLastClip() > 0.0f) && (audio->getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME)) { const float MAX_MAGNITUDE = 0.7f; float magnitude = MAX_MAGNITUDE * (1 - audio->getTimeSinceLastClip() / CLIPPING_INDICATOR_TIME); renderCollisionOverlay(glWidget->width(), glWidget->height(), magnitude, 1.0f); diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index c0d8261427..b6b43c4baf 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -48,7 +48,7 @@ enum GridPlane { const glm::vec2 INVALID_VECTOR(FLT_MAX, FLT_MAX); MetavoxelEditor::MetavoxelEditor() : - QWidget(Application::getInstance()->getGLWidget(), Qt::Tool) { + QWidget(Application::getInstance()->getWindow(), Qt::Tool) { setWindowTitle("Metavoxel Editor"); setAttribute(Qt::WA_DeleteOnClose); @@ -122,11 +122,9 @@ MetavoxelEditor::MetavoxelEditor() : addTool(new InsertSpannerTool(this)); addTool(new RemoveSpannerTool(this)); addTool(new ClearSpannersTool(this)); - addTool(new SetSpannerTool(this)); addTool(new HeightfieldHeightBrushTool(this)); addTool(new HeightfieldMaterialBrushTool(this)); addTool(new ImportHeightfieldTool(this)); - addTool(new EraseHeightfieldTool(this)); addTool(new VoxelMaterialBoxTool(this)); addTool(new VoxelMaterialSpannerTool(this)); addTool(new VoxelMaterialBrushTool(this)); @@ -136,6 +134,8 @@ MetavoxelEditor::MetavoxelEditor() : connect(Application::getInstance(), SIGNAL(simulating(float)), SLOT(simulate(float))); connect(Application::getInstance(), SIGNAL(renderingInWorldInterface()), SLOT(render())); + connect(Application::getInstance()->getMetavoxels(), &MetavoxelSystem::rendering, + this, &MetavoxelEditor::renderPreview); Application::getInstance()->getGLWidget()->installEventFilter(this); @@ -241,7 +241,7 @@ void MetavoxelEditor::createNewAttribute() { form.addRow("Name:", &name); SharedObjectEditor editor(&Attribute::staticMetaObject, false); - editor.setObject(new QRgbAttribute()); + editor.setObject(new FloatAttribute()); layout.addWidget(&editor); QDialogButtonBox buttons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); @@ -370,6 +370,13 @@ void MetavoxelEditor::render() { glDepthMask(GL_TRUE); } +void MetavoxelEditor::renderPreview() { + MetavoxelTool* tool = getActiveTool(); + if (tool) { + tool->renderPreview(); + } +} + void MetavoxelEditor::addTool(MetavoxelTool* tool) { _tools.append(tool); layout()->addWidget(tool); @@ -407,6 +414,10 @@ void MetavoxelTool::render() { // nothing by default } +void MetavoxelTool::renderPreview() { + // nothing by default +} + BoxTool::BoxTool(MetavoxelEditor* editor, const QString& name, bool usesValue, bool userFacing) : MetavoxelTool(editor, name, usesValue, userFacing) { @@ -587,6 +598,16 @@ void GlobalSetTool::apply() { PlaceSpannerTool::PlaceSpannerTool(MetavoxelEditor* editor, const QString& name, const QString& placeText, bool usesValue) : MetavoxelTool(editor, name, usesValue) { + QWidget* widget = new QWidget(this); + layout()->addWidget(widget); + QHBoxLayout* box = new QHBoxLayout(); + widget->setLayout(box); + box->setContentsMargins(QMargins()); + box->addStretch(1); + box->addWidget(_followMouse = new QCheckBox("Follow Mouse")); + _followMouse->setChecked(true); + box->addStretch(1); + if (!placeText.isEmpty()) { QPushButton* button = new QPushButton(placeText); layout()->addWidget(button); @@ -595,12 +616,9 @@ PlaceSpannerTool::PlaceSpannerTool(MetavoxelEditor* editor, const QString& name, } void PlaceSpannerTool::simulate(float deltaTime) { - if (Application::getInstance()->isMouseHidden()) { - return; - } Spanner* spanner = static_cast(getSpanner(true).data()); Transformable* transformable = qobject_cast(spanner); - if (transformable) { + if (transformable && _followMouse->isChecked() && !Application::getInstance()->isMouseHidden()) { // find the intersection of the mouse ray with the grid and place the transformable there glm::quat rotation = _editor->getGridRotation(); glm::quat inverseRotation = glm::inverse(rotation); @@ -614,15 +632,9 @@ void PlaceSpannerTool::simulate(float deltaTime) { spanner->getRenderer()->simulate(deltaTime); } -void PlaceSpannerTool::render() { - if (Application::getInstance()->isMouseHidden()) { - return; - } +void PlaceSpannerTool::renderPreview() { Spanner* spanner = static_cast(getSpanner().data()); - const float SPANNER_ALPHA = 0.25f; - QColor color = getColor(); - spanner->getRenderer()->render(glm::vec4(color.redF(), color.greenF(), color.blueF(), SPANNER_ALPHA), - SpannerRenderer::DEFAULT_MODE, glm::vec3(), 0.0f); + spanner->getRenderer()->render(); } bool PlaceSpannerTool::appliesTo(const AttributePointer& attribute) const { @@ -661,7 +673,7 @@ InsertSpannerTool::InsertSpannerTool(MetavoxelEditor* editor) : void InsertSpannerTool::applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) { MetavoxelEditMessage message = { QVariant::fromValue(InsertSpannerEdit(attribute, spanner)) }; - Application::getInstance()->getMetavoxels()->applyEdit(message); + Application::getInstance()->getMetavoxels()->applyEdit(message, true); } RemoveSpannerTool::RemoveSpannerTool(MetavoxelEditor* editor) : @@ -712,246 +724,6 @@ void ClearSpannersTool::clear() { Application::getInstance()->getMetavoxels()->applyEdit(message); } -SetSpannerTool::SetSpannerTool(MetavoxelEditor* editor) : - PlaceSpannerTool(editor, "Set Spanner", "Set") { -} - -bool SetSpannerTool::appliesTo(const AttributePointer& attribute) const { - return attribute == AttributeRegistry::getInstance()->getSpannersAttribute(); -} - -glm::quat DIRECTION_ROTATIONS[] = { - rotationBetween(glm::vec3(-1.0f, 0.0f, 0.0f), IDENTITY_FRONT), - rotationBetween(glm::vec3(1.0f, 0.0f, 0.0f), IDENTITY_FRONT), - rotationBetween(glm::vec3(0.0f, -1.0f, 0.0f), IDENTITY_FRONT), - rotationBetween(glm::vec3(0.0f, 1.0f, 0.0f), IDENTITY_FRONT), - rotationBetween(glm::vec3(0.0f, 0.0f, -1.0f), IDENTITY_FRONT), - rotationBetween(glm::vec3(0.0f, 0.0f, 1.0f), IDENTITY_FRONT) }; - -/// Represents a view from one direction of the spanner to be voxelized. -class DirectionImages { -public: - QImage color; - QVector depth; - glm::vec3 minima; - glm::vec3 maxima; - glm::vec3 scale; -}; - -class Voxelizer : public QRunnable { -public: - - Voxelizer(float size, const Box& bounds, float granularity, const QVector& directionImages); - - virtual void run(); - -private: - - void voxelize(const glm::vec3& center); - - float _size; - Box _bounds; - float _granularity; - QVector _directionImages; -}; - -Voxelizer::Voxelizer(float size, const Box& bounds, float granularity, const QVector& directionImages) : - _size(size), - _bounds(bounds), - _granularity(granularity), - _directionImages(directionImages) { -} - -void Voxelizer::run() { - // voxelize separately each cell within the bounds - float halfSize = _size * 0.5f; - for (float x = _bounds.minimum.x + halfSize; x < _bounds.maximum.x; x += _size) { - for (float y = _bounds.minimum.y + halfSize; y < _bounds.maximum.y; y += _size) { - for (float z = _bounds.minimum.z + halfSize; z < _bounds.maximum.z; z += _size) { - voxelize(glm::vec3(x, y, z)); - } - } - } -} - -class VoxelizationVisitor : public MetavoxelVisitor { -public: - - VoxelizationVisitor(const QVector& directionImages, const glm::vec3& center, float granularity); - - virtual int visit(MetavoxelInfo& info); - -private: - - QVector _directionImages; - glm::vec3 _center; - float _granularity; -}; - -VoxelizationVisitor::VoxelizationVisitor(const QVector& directionImages, - const glm::vec3& center, float granularity) : - MetavoxelVisitor(QVector(), QVector() << - AttributeRegistry::getInstance()->getColorAttribute()), - _directionImages(directionImages), - _center(center), - _granularity(granularity) { -} - -bool checkDisjoint(const DirectionImages& images, const glm::vec3& minimum, const glm::vec3& maximum, float extent) { - for (int x = qMax(0, (int)minimum.x), xmax = qMin(images.color.width(), (int)maximum.x); x < xmax; x++) { - for (int y = qMax(0, (int)minimum.y), ymax = qMin(images.color.height(), (int)maximum.y); y < ymax; y++) { - float depth = 1.0f - images.depth.at(y * images.color.width() + x); - if (depth - minimum.z >= -extent - EPSILON) { - return false; - } - } - } - return true; -} - -int VoxelizationVisitor::visit(MetavoxelInfo& info) { - float halfSize = info.size * 0.5f; - glm::vec3 center = info.minimum + _center + glm::vec3(halfSize, halfSize, halfSize); - const float EXTENT_SCALE = 2.0f; - if (info.size > _granularity) { - for (unsigned int i = 0; i < sizeof(DIRECTION_ROTATIONS) / sizeof(DIRECTION_ROTATIONS[0]); i++) { - glm::vec3 rotated = DIRECTION_ROTATIONS[i] * center; - const DirectionImages& images = _directionImages.at(i); - glm::vec3 relative = (rotated - images.minima) * images.scale; - glm::vec3 extents = images.scale * halfSize; - glm::vec3 minimum = relative - extents; - glm::vec3 maximum = relative + extents; - if (checkDisjoint(images, minimum, maximum, extents.z * EXTENT_SCALE)) { - info.outputValues[0] = AttributeValue(_outputs.at(0)); - return STOP_RECURSION; - } - } - return DEFAULT_ORDER; - } - QRgb closestColor = QRgb(); - float closestDistance = FLT_MAX; - for (unsigned int i = 0; i < sizeof(DIRECTION_ROTATIONS) / sizeof(DIRECTION_ROTATIONS[0]); i++) { - glm::vec3 rotated = DIRECTION_ROTATIONS[i] * center; - const DirectionImages& images = _directionImages.at(i); - glm::vec3 relative = (rotated - images.minima) * images.scale; - int x = qMax(qMin((int)glm::round(relative.x), images.color.width() - 1), 0); - int y = qMax(qMin((int)glm::round(relative.y), images.color.height() - 1), 0); - float depth = 1.0f - images.depth.at(y * images.color.width() + x); - float distance = depth - relative.z; - float extent = images.scale.z * halfSize * EXTENT_SCALE; - if (distance < -extent - EPSILON) { - info.outputValues[0] = AttributeValue(_outputs.at(0)); - return STOP_RECURSION; - } - QRgb color = images.color.pixel(x, y); - if (distance < extent + EPSILON) { - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(color)); - return STOP_RECURSION; - } - if (distance < closestDistance) { - closestColor = color; - closestDistance = distance; - } - } - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(closestColor)); - return STOP_RECURSION; -} - -void Voxelizer::voxelize(const glm::vec3& center) { - MetavoxelData data; - data.setSize(_size); - - VoxelizationVisitor visitor(_directionImages, center, _granularity); - data.guide(visitor); - - MetavoxelEditMessage edit = { QVariant::fromValue(SetDataEdit( - center - glm::vec3(_size, _size, _size) * 0.5f, data, true)) }; - QMetaObject::invokeMethod(Application::getInstance()->getMetavoxels(), "applyEdit", - Q_ARG(const MetavoxelEditMessage&, edit), Q_ARG(bool, true)); -} - -void SetSpannerTool::applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) { - Spanner* spannerData = static_cast(spanner.data()); - Box bounds = spannerData->getBounds(); - float longestSide(qMax(bounds.getLongestSide(), spannerData->getPlacementGranularity())); - float size = powf(2.0f, floorf(logf(longestSide) / logf(2.0f))); - Box cellBounds(glm::floor(bounds.minimum / size) * size, glm::ceil(bounds.maximum / size) * size); - - Application::getInstance()->getTextureCache()->getPrimaryFramebufferObject()->bind(); - - glEnable(GL_SCISSOR_TEST); - glEnable(GL_LIGHTING); - glEnable(GL_DEPTH_TEST); - glDisable(GL_BLEND); - - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - - QVector directionImages; - - for (unsigned int i = 0; i < sizeof(DIRECTION_ROTATIONS) / sizeof(DIRECTION_ROTATIONS[0]); i++) { - glm::vec3 minima(FLT_MAX, FLT_MAX, FLT_MAX); - glm::vec3 maxima(-FLT_MAX, -FLT_MAX, -FLT_MAX); - for (int j = 0; j < Box::VERTEX_COUNT; j++) { - glm::vec3 rotated = DIRECTION_ROTATIONS[i] * cellBounds.getVertex(j); - minima = glm::min(minima, rotated); - maxima = glm::max(maxima, rotated); - } - float renderGranularity = spannerData->getVoxelizationGranularity() / 4.0f; - int width = glm::round((maxima.x - minima.x) / renderGranularity); - int height = glm::round((maxima.y - minima.y) / renderGranularity); - - glViewport(0, 0, width, height); - glScissor(0, 0, width, height); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - glLoadIdentity(); - glOrtho(minima.x, maxima.x, minima.y, maxima.y, -maxima.z, -minima.z); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glm::vec3 axis = glm::axis(DIRECTION_ROTATIONS[i]); - glRotatef(glm::degrees(glm::angle(DIRECTION_ROTATIONS[i])), axis.x, axis.y, axis.z); - - Application::getInstance()->setupWorldLight(); - - Application::getInstance()->updateUntranslatedViewMatrix(); - // TODO: assign an equivalent viewTransform object to the application to match the current path which uses glMatrixStack - // setViewTransform(viewTransform); - - const glm::vec4 OPAQUE_WHITE(1.0f, 1.0f, 1.0f, 1.0f); - spannerData->getRenderer()->render(OPAQUE_WHITE, SpannerRenderer::DIFFUSE_MODE, glm::vec3(), 0.0f); - - DirectionImages images = { QImage(width, height, QImage::Format_ARGB32), - QVector(width * height), minima, maxima, glm::vec3(width / (maxima.x - minima.x), - height / (maxima.y - minima.y), 1.0f / (maxima.z - minima.z)) }; - glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, images.color.bits()); - glReadPixels(0, 0, width, height, GL_DEPTH_COMPONENT, GL_FLOAT, images.depth.data()); - directionImages.append(images); - - glMatrixMode(GL_PROJECTION); - } - glPopMatrix(); - - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - - glEnable(GL_BLEND); - glDisable(GL_SCISSOR_TEST); - - Application::getInstance()->getTextureCache()->getPrimaryFramebufferObject()->release(); - - glViewport(0, 0, Application::getInstance()->getGLWidget()->getDeviceWidth(), - Application::getInstance()->getGLWidget()->getDeviceHeight()); - - // send the images off to the lab for processing - QThreadPool::globalInstance()->start(new Voxelizer(size, cellBounds, - spannerData->getVoxelizationGranularity(), directionImages)); -} - HeightfieldTool::HeightfieldTool(MetavoxelEditor* editor, const QString& name) : MetavoxelTool(editor, name, false) { @@ -964,7 +736,7 @@ HeightfieldTool::HeightfieldTool(MetavoxelEditor* editor, const QString& name) : _scale->setMinimum(-FLT_MAX); _scale->setMaximum(FLT_MAX); _scale->setPrefix("2^"); - _scale->setValue(3.0); + _scale->setValue(2.0); QPushButton* applyButton = new QPushButton("Apply"); layout()->addWidget(applyButton); @@ -972,7 +744,7 @@ HeightfieldTool::HeightfieldTool(MetavoxelEditor* editor, const QString& name) : } bool HeightfieldTool::appliesTo(const AttributePointer& attribute) const { - return attribute->inherits("HeightfieldAttribute"); + return attribute->inherits("SpannerSetAttribute"); } void HeightfieldTool::render() { @@ -984,274 +756,131 @@ void HeightfieldTool::render() { ImportHeightfieldTool::ImportHeightfieldTool(MetavoxelEditor* editor) : HeightfieldTool(editor, "Import Heightfield"), - _loadingImage(false) { + _spanner(new Heightfield()) { - _form->addRow("Block Size:", _blockSize = new QSpinBox()); - _blockSize->setPrefix("2^"); - _blockSize->setMinimum(1); - _blockSize->setValue(5); - - connect(_blockSize, static_cast(&QSpinBox::valueChanged), this, - &ImportHeightfieldTool::updatePreview); - _form->addRow("Height:", _height = new QPushButton()); - connect(_height, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectHeightFile); - - _form->addRow(_rawOptions = new QWidget()); - QHBoxLayout* rawLayout = new QHBoxLayout(); - _rawOptions->setLayout(rawLayout); - _rawOptions->setVisible(false); - - rawLayout->addStretch(1); - rawLayout->addWidget(new QLabel("Scale:")); - rawLayout->addWidget(_heightScale = new QDoubleSpinBox()); + _form->addRow("Height Scale:", _heightScale = new QDoubleSpinBox()); const double MAX_OFFSET_SCALE = 100000.0; _heightScale->setMaximum(MAX_OFFSET_SCALE); - _heightScale->setSingleStep(0.0001); - _heightScale->setDecimals(4); - _heightScale->setValue(1.0); + _heightScale->setSingleStep(0.01); + _heightScale->setValue(8.0); connect(_heightScale, static_cast(&QDoubleSpinBox::valueChanged), this, - &ImportHeightfieldTool::updateHeightImage); - - rawLayout->addSpacing(15); - rawLayout->addWidget(new QLabel("Offset:")); - rawLayout->addWidget(_heightOffset = new QDoubleSpinBox()); + &ImportHeightfieldTool::updateSpanner); + + _form->addRow("Height Offset:", _heightOffset = new QDoubleSpinBox()); _heightOffset->setMinimum(-MAX_OFFSET_SCALE); _heightOffset->setMaximum(MAX_OFFSET_SCALE); - _heightOffset->setDecimals(4); + _heightOffset->setSingleStep(0.01); connect(_heightOffset, static_cast(&QDoubleSpinBox::valueChanged), this, - &ImportHeightfieldTool::updateHeightImage); + &ImportHeightfieldTool::updateSpanner); - _form->addRow("Color:", _color = new QPushButton()); - connect(_color, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectColorFile); + _form->addRow("Height:", _height = new HeightfieldHeightEditor(this)); + connect(_height, &HeightfieldHeightEditor::heightChanged, this, &ImportHeightfieldTool::updateSpanner); - connect(Application::getInstance()->getMetavoxels(), &MetavoxelSystem::rendering, - this, &ImportHeightfieldTool::renderPreview); + _form->addRow("Color:", _color = new HeightfieldColorEditor(this)); + connect(_color, &HeightfieldColorEditor::colorChanged, this, &ImportHeightfieldTool::updateSpanner); + + connect(_translation, &Vec3Editor::valueChanged, this, &ImportHeightfieldTool::updateSpanner); + connect(_scale, static_cast(&QDoubleSpinBox::valueChanged), this, + &ImportHeightfieldTool::updateSpanner); } -void ImportHeightfieldTool::apply() { - float scale = _translation->getSingleStep(); - foreach (const BufferDataPointer& bufferData, _preview.getBuffers()) { - HeightfieldBuffer* buffer = static_cast(bufferData.data()); - MetavoxelData data; - data.setSize(scale); - - QByteArray height = buffer->getUnextendedHeight(); - HeightfieldHeightDataPointer heightPointer(new HeightfieldHeightData(height)); - data.setRoot(AttributeRegistry::getInstance()->getHeightfieldAttribute(), new MetavoxelNode(AttributeValue( - AttributeRegistry::getInstance()->getHeightfieldAttribute(), encodeInline(heightPointer)))); - - MetavoxelEditMessage message = { QVariant::fromValue(SetDataEdit( - _translation->getValue() + buffer->getTranslation() * scale, data)) }; - Application::getInstance()->getMetavoxels()->applyEdit(message, true); - - int colorUnits = buffer->getColor().isEmpty() ? 1 : (buffer->getColorSize() - HeightfieldBuffer::SHARED_EDGE) / - (buffer->getHeightSize() - HeightfieldBuffer::HEIGHT_EXTENSION); - float colorScale = scale / colorUnits; - - for (int y = 0; y < colorUnits; y++) { - for (int x = 0; x < colorUnits; x++) { - MetavoxelData data; - data.setSize(colorScale); - - QByteArray color; - if (buffer->getColor().isEmpty()) { - const unsigned char WHITE_VALUE = 0xFF; - color = QByteArray(height.size() * DataBlock::COLOR_BYTES, WHITE_VALUE); - } else { - color = buffer->getUnextendedColor(x, y); - } - HeightfieldColorDataPointer colorPointer(new HeightfieldColorData(color)); - data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode( - AttributeValue(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), - encodeInline(colorPointer)))); - - QByteArray material(height.size(), 0); - HeightfieldMaterialDataPointer materialPointer(new HeightfieldMaterialData(material)); - data.setRoot(AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), new MetavoxelNode( - AttributeValue(AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), - encodeInline(materialPointer)))); - - MetavoxelEditMessage message = { QVariant::fromValue(SetDataEdit( - _translation->getValue() + buffer->getTranslation() * scale + glm::vec3(x, 0.0f, y) * colorScale, data)) }; - Application::getInstance()->getMetavoxels()->applyEdit(message, true); - } - } - } -} - -const float EIGHT_BIT_MAXIMUM = 255.0f; - -void ImportHeightfieldTool::selectHeightFile() { - QString filename = QFileDialog::getOpenFileName(this, "Select Height Image", QString(), - "Images (*.png *.jpg *.bmp *.raw)"); - if (filename.isNull()) { - return; - } - if (filename.toLower().endsWith(".raw")) { - QFile input(filename); - input.open(QIODevice::ReadOnly); - QDataStream in(&input); - in.setByteOrder(QDataStream::LittleEndian); - _rawHeight.clear(); - int minHeight = numeric_limits::max(); - int maxHeight = numeric_limits::min(); - while (!in.atEnd()) { - quint16 height; - in >> height; - _rawHeight.append(height); - minHeight = qMin(minHeight, (int)height); - maxHeight = qMax(maxHeight, (int)height); - } - _height->setText(filename); - _rawOptions->setVisible(true); - _loadingImage = true; - _heightScale->setValue((EIGHT_BIT_MAXIMUM - 1.0f) / (maxHeight - minHeight)); - _heightOffset->setValue(-minHeight * _heightScale->value() + 1.0); - _loadingImage = false; - updateHeightImage(); - return; - } - if (!_heightImage.load(filename)) { - QMessageBox::warning(this, "Invalid Image", "The selected image could not be read."); - return; - } - _rawOptions->setVisible(false); - _heightImage = _heightImage.convertToFormat(QImage::Format_RGB888); - _height->setText(filename); - updatePreview(); -} - -void ImportHeightfieldTool::selectColorFile() { - QString filename = QFileDialog::getOpenFileName(this, "Select Color Image", QString(), "Images (*.png *.jpg *.bmp)"); - if (filename.isNull()) { - return; - } - if (!_colorImage.load(filename)) { - QMessageBox::warning(this, "Invalid Image", "The selected image could not be read."); - return; - } - _colorImage = _colorImage.convertToFormat(QImage::Format_RGB888); - _color->setText(filename); - updatePreview(); -} - -void ImportHeightfieldTool::updateHeightImage() { - if (_loadingImage) { - return; - } - int size = glm::sqrt(float(_rawHeight.size())); - _heightImage = QImage(size, size, QImage::Format_RGB888); - const quint16* src = _rawHeight.constData(); - float scale = _heightScale->value(), offset = _heightOffset->value(); - for (int y = 0; y < size; y++) { - uchar* dest = _heightImage.scanLine(y); - for (const quint16* end = src + size; src != end; src++) { - uchar height = glm::clamp(*src * scale + offset, 1.0f, EIGHT_BIT_MAXIMUM); - *dest++ = height; - *dest++ = height; - *dest++ = height; - } - } - updatePreview(); -} - -void ImportHeightfieldTool::updatePreview() { - QVector buffers; - if (_heightImage.width() > 0 && _heightImage.height() > 0) { - float z = 0.0f; - int blockSize = pow(2.0, _blockSize->value()); - int heightSize = blockSize + HeightfieldBuffer::HEIGHT_EXTENSION; - int colorScale = glm::round(glm::log(_colorImage.height() / (float)_heightImage.height()) / glm::log(2.0f)); - int colorBlockSize = blockSize * pow(2.0, qMax(colorScale, 0)); - int colorSize = colorBlockSize + HeightfieldBuffer::SHARED_EDGE; - for (int i = 0; i < _heightImage.height(); i += blockSize, z++) { - float x = 0.0f; - for (int j = 0; j < _heightImage.width(); j += blockSize, x++) { - QByteArray height(heightSize * heightSize, 0); - int extendedI = qMax(i - HeightfieldBuffer::HEIGHT_BORDER, 0); - int extendedJ = qMax(j - HeightfieldBuffer::HEIGHT_BORDER, 0); - int offsetY = extendedI - i + HeightfieldBuffer::HEIGHT_BORDER; - int offsetX = extendedJ - j + HeightfieldBuffer::HEIGHT_BORDER; - int rows = qMin(heightSize - offsetY, _heightImage.height() - extendedI); - int columns = qMin(heightSize - offsetX, _heightImage.width() - extendedJ); - for (int y = 0; y < rows; y++) { - uchar* src = _heightImage.scanLine(extendedI + y) + extendedJ * DataBlock::COLOR_BYTES; - char* dest = height.data() + (y + offsetY) * heightSize + offsetX; - for (int x = 0; x < columns; x++) { - *dest++ = qMax((uchar)1, *src); - src += DataBlock::COLOR_BYTES; - } - } - QByteArray color; - if (!_colorImage.isNull()) { - int colorI = (i / blockSize) * colorBlockSize; - int colorJ = (j / blockSize) * colorBlockSize; - color = QByteArray(colorSize * colorSize * DataBlock::COLOR_BYTES, 0); - rows = qMax(0, qMin(colorSize, _colorImage.height() - colorI)); - columns = qMax(0, qMin(colorSize, _colorImage.width() - colorJ)); - for (int y = 0; y < rows; y++) { - memcpy(color.data() + y * colorSize * DataBlock::COLOR_BYTES, - _colorImage.scanLine(colorI + y) + colorJ * DataBlock::COLOR_BYTES, - columns * DataBlock::COLOR_BYTES); - } - } - buffers.append(BufferDataPointer(new HeightfieldBuffer(glm::vec3(x, 0.0f, z), 1.0f, height, color))); - } - } - } - _preview.setBuffers(buffers); +void ImportHeightfieldTool::simulate(float deltaTime) { + static_cast(_spanner.data())->getRenderer()->simulate(deltaTime); } void ImportHeightfieldTool::renderPreview() { - if (isVisible()) { - _preview.render(_translation->getValue(), _translation->getSingleStep()); + static_cast(_spanner.data())->getRenderer()->render(); +} + +const int HEIGHTFIELD_BLOCK_SIZE = 256; + +void ImportHeightfieldTool::apply() { + AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute()); + if (!(_height->getHeight() && attribute)) { + return; + } + int width = _height->getHeight()->getWidth(); + const QVector& contents = _height->getHeight()->getContents(); + int height = contents.size() / width; + int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION; + int innerHeight = height - HeightfieldHeight::HEIGHT_EXTENSION; + float scale = pow(2.0, _scale->value()); + + for (int i = 0; i < innerHeight; i += HEIGHTFIELD_BLOCK_SIZE) { + for (int j = 0; j < innerWidth; j += HEIGHTFIELD_BLOCK_SIZE) { + Heightfield* heightfield = new Heightfield(); + + int extendedHeightSize = HEIGHTFIELD_BLOCK_SIZE + HeightfieldHeight::HEIGHT_EXTENSION; + QVector heightContents(extendedHeightSize * extendedHeightSize); + quint16* dest = heightContents.data(); + const quint16* src = contents.constData() + i * width + j; + int copyWidth = qMin(width - j, extendedHeightSize); + int copyHeight = qMin(height - i, extendedHeightSize); + for (int z = 0; z < copyHeight; z++, src += width, dest += extendedHeightSize) { + memcpy(dest, src, copyWidth * sizeof(quint16)); + } + heightfield->setHeight(HeightfieldHeightPointer(new HeightfieldHeight(extendedHeightSize, heightContents))); + + int materialWidth = HEIGHTFIELD_BLOCK_SIZE + HeightfieldData::SHARED_EDGE; + int materialHeight = materialWidth; + if (_color->getColor()) { + int colorWidth = _color->getColor()->getWidth(); + const QByteArray& contents = _color->getColor()->getContents(); + int colorHeight = contents.size() / (colorWidth * DataBlock::COLOR_BYTES); + int innerColorWidth = colorWidth - HeightfieldData::SHARED_EDGE; + int innerColorHeight = colorHeight - HeightfieldData::SHARED_EDGE; + materialWidth = HEIGHTFIELD_BLOCK_SIZE * innerColorWidth / innerWidth + HeightfieldData::SHARED_EDGE; + materialHeight = HEIGHTFIELD_BLOCK_SIZE * innerColorHeight / innerHeight + HeightfieldData::SHARED_EDGE; + QByteArray colorContents(materialWidth * materialHeight * DataBlock::COLOR_BYTES, 0); + int colorI = i * (materialWidth - HeightfieldData::SHARED_EDGE) / HEIGHTFIELD_BLOCK_SIZE; + int colorJ = j * (materialHeight - HeightfieldData::SHARED_EDGE) / HEIGHTFIELD_BLOCK_SIZE; + char* dest = colorContents.data(); + const char* src = contents.constData() + (colorI * colorWidth + colorJ) * DataBlock::COLOR_BYTES; + int copyWidth = qMin(colorWidth - colorJ, materialWidth); + int copyHeight = qMin(colorHeight - colorI, materialHeight); + for (int z = 0; z < copyHeight; z++, src += colorWidth * DataBlock::COLOR_BYTES, + dest += materialWidth * DataBlock::COLOR_BYTES) { + memcpy(dest, src, copyWidth * DataBlock::COLOR_BYTES); + } + heightfield->setColor(HeightfieldColorPointer(new HeightfieldColor(materialWidth, colorContents))); + + } else { + heightfield->setColor(HeightfieldColorPointer(new HeightfieldColor(materialWidth, + QByteArray(materialWidth * materialHeight * DataBlock::COLOR_BYTES, 0xFF)))); + } + heightfield->setMaterial(HeightfieldMaterialPointer(new HeightfieldMaterial(materialWidth, + QByteArray(materialWidth * materialHeight, 0), QVector()))); + + heightfield->setScale(scale); + heightfield->setAspectY(_heightScale->value() / scale); + heightfield->setTranslation(_translation->getValue() + glm::vec3((j / HEIGHTFIELD_BLOCK_SIZE) * scale, + _heightOffset->value(), (i / HEIGHTFIELD_BLOCK_SIZE) * scale)); + + MetavoxelEditMessage message = { QVariant::fromValue(InsertSpannerEdit(attribute, heightfield)) }; + Application::getInstance()->getMetavoxels()->applyEdit(message, true); + } } } -EraseHeightfieldTool::EraseHeightfieldTool(MetavoxelEditor* editor) : - HeightfieldTool(editor, "Erase Heightfield") { +void ImportHeightfieldTool::updateSpanner() { + Heightfield* heightfield = static_cast(_spanner.data()); + heightfield->setHeight(_height->getHeight()); + heightfield->setColor(_color->getColor()); - _form->addRow("Width:", _width = new QSpinBox()); - _width->setMinimum(1); - _width->setMaximum(INT_MAX); - _form->addRow("Length:", _length = new QSpinBox()); - _length->setMinimum(1); - _length->setMaximum(INT_MAX); -} - -void EraseHeightfieldTool::render() { - HeightfieldTool::render(); - - glColor3f(1.0f, 0.0f, 0.0f); - glLineWidth(4.0f); - - glPushMatrix(); - glm::vec3 translation = _translation->getValue(); - glTranslatef(translation.x, translation.y, translation.z); - float scale = _translation->getSingleStep(); - glScalef(scale * _width->value(), scale, scale * _length->value()); - glTranslatef(0.5f, 0.5f, 0.5f); - - glutWireCube(1.0); - - glPopMatrix(); - - glLineWidth(1.0f); -} - -void EraseHeightfieldTool::apply() { - // clear the heightfield - float scale = _translation->getSingleStep(); - BoxSetEdit edit(Box(_translation->getValue(), _translation->getValue() + - glm::vec3(_width->value() * scale, scale, _length->value() * scale)), scale, - OwnedAttributeValue(AttributeRegistry::getInstance()->getHeightfieldAttribute())); - MetavoxelEditMessage message = { QVariant::fromValue(edit) }; - Application::getInstance()->getMetavoxels()->applyEdit(message, true); - - // and the color - edit.value = OwnedAttributeValue(AttributeRegistry::getInstance()->getHeightfieldColorAttribute()); - message.edit = QVariant::fromValue(edit); - Application::getInstance()->getMetavoxels()->applyEdit(message, true); + float scale = pow(2.0, _scale->value()); + float aspectZ = 1.0f; + if (_height->getHeight()) { + int width = _height->getHeight()->getWidth(); + int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION; + int innerHeight = _height->getHeight()->getContents().size() / width - HeightfieldHeight::HEIGHT_EXTENSION; + float widthBlocks = glm::ceil((float)innerWidth / HEIGHTFIELD_BLOCK_SIZE); + scale *= widthBlocks; + aspectZ = glm::ceil((float)innerHeight / HEIGHTFIELD_BLOCK_SIZE) / widthBlocks; + } + heightfield->setScale(scale); + heightfield->setAspectY(_heightScale->value() / scale); + heightfield->setAspectZ(aspectZ); + heightfield->setTranslation(_translation->getValue() + glm::vec3(0.0f, _heightOffset->value(), 0.0f)); } HeightfieldBrushTool::HeightfieldBrushTool(MetavoxelEditor* editor, const QString& name) : @@ -1268,7 +897,7 @@ HeightfieldBrushTool::HeightfieldBrushTool(MetavoxelEditor* editor, const QStrin } bool HeightfieldBrushTool::appliesTo(const AttributePointer& attribute) const { - return attribute->inherits("HeightfieldAttribute"); + return attribute->inherits("SpannerSetAttribute"); } void HeightfieldBrushTool::render() { diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index d17834ae18..cccb41ecfc 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -62,6 +62,7 @@ private slots: void simulate(float deltaTime); void render(); + void renderPreview(); private: @@ -104,6 +105,9 @@ public: /// Renders the tool's interface, if any. virtual void render(); + /// Renders the tool's metavoxel preview, if any. + virtual void renderPreview(); + protected: MetavoxelEditor* _editor; @@ -184,7 +188,7 @@ public: virtual void simulate(float deltaTime); - virtual void render(); + virtual void renderPreview(); virtual bool appliesTo(const AttributePointer& attribute) const; @@ -199,6 +203,10 @@ protected: protected slots: void place(); + +private: + + QCheckBox* _followMouse; }; /// Allows inserting a spanner into the scene. @@ -242,21 +250,6 @@ private slots: void clear(); }; -/// Allows setting the value by placing a spanner. -class SetSpannerTool : public PlaceSpannerTool { - Q_OBJECT - -public: - - SetSpannerTool(MetavoxelEditor* editor); - - virtual bool appliesTo(const AttributePointer& attribute) const; - -protected: - - virtual void applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner); -}; - /// Base class for heightfield tools. class HeightfieldTool : public MetavoxelTool { Q_OBJECT @@ -288,55 +281,27 @@ public: ImportHeightfieldTool(MetavoxelEditor* editor); + virtual void simulate(float deltaTime); + + virtual void renderPreview(); + protected: virtual void apply(); private slots: - void selectHeightFile(); - void selectColorFile(); - void updateHeightImage(); - void updatePreview(); - void renderPreview(); - + void updateSpanner(); + private: - QSpinBox* _blockSize; - - QPushButton* _height; - QWidget* _rawOptions; QDoubleSpinBox* _heightScale; QDoubleSpinBox* _heightOffset; - bool _loadingImage; - QPushButton* _color; + HeightfieldHeightEditor* _height; + HeightfieldColorEditor* _color; - QVector _rawHeight; - QImage _heightImage; - QImage _colorImage; - - HeightfieldPreview _preview; -}; - -/// Allows clearing heighfield blocks. -class EraseHeightfieldTool : public HeightfieldTool { - Q_OBJECT - -public: - - EraseHeightfieldTool(MetavoxelEditor* editor); - - virtual void render(); - -protected: - - virtual void apply(); - -private: - - QSpinBox* _width; - QSpinBox* _length; + SharedObjectPointer _spanner; }; /// Base class for tools that allow painting on heightfields. diff --git a/interface/src/ui/OctreeStatsDialog.cpp b/interface/src/ui/OctreeStatsDialog.cpp index e51b9b1f42..66a4ce8409 100644 --- a/interface/src/ui/OctreeStatsDialog.cpp +++ b/interface/src/ui/OctreeStatsDialog.cpp @@ -130,22 +130,22 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) { // Voxels Rendered label = _labels[_voxelsRendered]; - statsValue << "Max: " << voxels->getMaxVoxels() / 1000.f << "K " << - "Drawn: " << voxels->getVoxelsWritten() / 1000.f << "K " << - "Abandoned: " << voxels->getAbandonedVoxels() / 1000.f << "K " << - "ReadBuffer: " << voxels->getVoxelsRendered() / 1000.f << "K " << - "Changed: " << voxels->getVoxelsUpdated() / 1000.f << "K "; + statsValue << "Max: " << voxels->getMaxVoxels() / 1000.0f << "K " << + "Drawn: " << voxels->getVoxelsWritten() / 1000.0f << "K " << + "Abandoned: " << voxels->getAbandonedVoxels() / 1000.0f << "K " << + "ReadBuffer: " << voxels->getVoxelsRendered() / 1000.0f << "K " << + "Changed: " << voxels->getVoxelsUpdated() / 1000.0f << "K "; label->setText(statsValue.str().c_str()); // Voxels Memory Usage label = _labels[_localVoxelsMemory]; statsValue.str(""); statsValue << - "Elements RAM: " << OctreeElement::getTotalMemoryUsage() / 1000000.f << "MB " - "Geometry RAM: " << voxels->getVoxelMemoryUsageRAM() / 1000000.f << "MB " << - "VBO: " << voxels->getVoxelMemoryUsageVBO() / 1000000.f << "MB "; + "Elements RAM: " << OctreeElement::getTotalMemoryUsage() / 1000000.0f << "MB " + "Geometry RAM: " << voxels->getVoxelMemoryUsageRAM() / 1000000.0f << "MB " << + "VBO: " << voxels->getVoxelMemoryUsageVBO() / 1000000.0f << "MB "; if (voxels->hasVoxelMemoryUsageGPU()) { - statsValue << "GPU: " << voxels->getVoxelMemoryUsageGPU() / 1000000.f << "MB "; + statsValue << "GPU: " << voxels->getVoxelMemoryUsageGPU() / 1000000.0f << "MB "; } label->setText(statsValue.str().c_str()); diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 9bad475838..e0e589b627 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -264,7 +264,7 @@ void Stats::display( char packetsPerSecondString[30]; sprintf(packetsPerSecondString, "Pkts/sec: %d", packetsPerSecond); char averageMegabitsPerSecond[30]; - sprintf(averageMegabitsPerSecond, "Mbps: %3.2f", (float)bytesPerSecond * 8.f / 1000000.f); + sprintf(averageMegabitsPerSecond, "Mbps: %3.2f", (float)bytesPerSecond * 8.0f / 1000000.0f); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, packetsPerSecondString, color); @@ -501,24 +501,24 @@ void Stats::display( voxelStats.str(""); voxelStats.precision(4); - voxelStats << "Voxels Drawn: " << voxels->getVoxelsWritten() / 1000.f << "K " << - "Abandoned: " << voxels->getAbandonedVoxels() / 1000.f << "K "; + voxelStats << "Voxels Drawn: " << voxels->getVoxelsWritten() / 1000.0f << "K " << + "Abandoned: " << voxels->getAbandonedVoxels() / 1000.0f << "K "; verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color); if (_expanded) { // Local Voxel Memory Usage voxelStats.str(""); - voxelStats << " Voxels Memory Nodes: " << VoxelTreeElement::getTotalMemoryUsage() / 1000000.f << "MB"; + voxelStats << " Voxels Memory Nodes: " << VoxelTreeElement::getTotalMemoryUsage() / 1000000.0f << "MB"; verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color); voxelStats.str(""); voxelStats << - " Geometry RAM: " << voxels->getVoxelMemoryUsageRAM() / 1000000.f << "MB / " << - "VBO: " << voxels->getVoxelMemoryUsageVBO() / 1000000.f << "MB"; + " Geometry RAM: " << voxels->getVoxelMemoryUsageRAM() / 1000000.0f << "MB / " << + "VBO: " << voxels->getVoxelMemoryUsageVBO() / 1000000.0f << "MB"; if (voxels->hasVoxelMemoryUsageGPU()) { - voxelStats << " / GPU: " << voxels->getVoxelMemoryUsageGPU() / 1000000.f << "MB"; + voxelStats << " / GPU: " << voxels->getVoxelMemoryUsageGPU() / 1000000.0f << "MB"; } verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color); @@ -526,7 +526,7 @@ void Stats::display( // Voxel Rendering voxelStats.str(""); voxelStats.precision(4); - voxelStats << " Voxel Rendering Slots Max: " << voxels->getMaxVoxels() / 1000.f << "K"; + voxelStats << " Voxel Rendering Slots Max: " << voxels->getMaxVoxels() / 1000.0f << "K"; verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color); } @@ -746,7 +746,7 @@ void Stats::display( audioReflector->getEchoesAttenuation()); verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, reflectionsStatus, color); + drawText(horizontalOffset, verticalOffset, 0.10f, 0.0f, 2.0f, reflectionsStatus, color); } } diff --git a/interface/src/ui/TextRenderer.cpp b/interface/src/ui/TextRenderer.cpp index 395847f265..edc14dabf5 100644 --- a/interface/src/ui/TextRenderer.cpp +++ b/interface/src/ui/TextRenderer.cpp @@ -73,12 +73,12 @@ int TextRenderer::draw(int x, int y, const char* str, float alpha) { // Grab the current color float currentColor[4]; glGetFloatv(GL_CURRENT_COLOR, currentColor); - alpha = std::max(0.f, std::min(alpha, 1.f)); + alpha = std::max(0.0f, std::min(alpha, 1.0f)); currentColor[3] *= alpha; - int compactColor = ((int(currentColor[0] * 255.f) & 0xFF)) | - ((int(currentColor[1] * 255.f) & 0xFF) << 8) | - ((int(currentColor[2] * 255.f) & 0xFF) << 16) | - ((int(currentColor[3] * 255.f) & 0xFF) << 24); + int compactColor = ((int(currentColor[0] * 255.0f) & 0xFF)) | + ((int(currentColor[1] * 255.0f) & 0xFF) << 8) | + ((int(currentColor[2] * 255.0f) & 0xFF) << 16) | + ((int(currentColor[3] * 255.0f) & 0xFF) << 24); // TODO: Remove that code once we test for performance improvments //glEnable(GL_TEXTURE_2D); diff --git a/interface/src/ui/TextRenderer.h b/interface/src/ui/TextRenderer.h index 325420cf2a..9afb885e35 100644 --- a/interface/src/ui/TextRenderer.h +++ b/interface/src/ui/TextRenderer.h @@ -63,7 +63,7 @@ public: int calculateHeight(const char* str); // also returns the height of the tallest character - int draw(int x, int y, const char* str, float alpha = 1.f); + int draw(int x, int y, const char* str, float alpha = 1.0f); int computeWidth(char ch); int computeWidth(const char* str); diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp index 88ac009ac7..dbb423ad99 100644 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -75,13 +75,14 @@ void Text3DOverlay::render(RenderArgs* args) { glPushMatrix(); { glTranslatef(_position.x, _position.y, _position.z); glm::quat rotation; + if (_isFacingAvatar) { // rotate about vertical to face the camera rotation = Application::getInstance()->getCamera()->getRotation(); - rotation *= glm::angleAxis(glm::pi(), glm::vec3(0.0f, 1.0f, 0.0f)); } else { rotation = getRotation(); } + glm::vec3 axis = glm::axis(rotation); glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index f965a8fa73..95432bc81a 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -38,7 +38,7 @@ AvatarData::AvatarData() : _position(0.0f), _handPosition(0.0f), _referential(NULL), - _bodyYaw(-90.f), + _bodyYaw(-90.0f), _bodyPitch(0.0f), _bodyRoll(0.0f), _targetScale(1.0f), diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index c8e5e61d49..d590f95bfd 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -83,7 +83,7 @@ const int IS_FACESHIFT_CONNECTED = 4; // 5th bit const int IS_CHAT_CIRCLING_ENABLED = 5; // 6th bit const int HAS_REFERENTIAL = 6; // 7th bit -static const float MAX_AVATAR_SCALE = 1000.f; +static const float MAX_AVATAR_SCALE = 1000.0f; static const float MIN_AVATAR_SCALE = .005f; const float MAX_AUDIO_LOUDNESS = 1000.0; // close enough for mouth animation @@ -281,7 +281,7 @@ public: QElapsedTimer& getLastUpdateTimer() { return _lastUpdateTimer; } - virtual float getBoundingRadius() const { return 1.f; } + virtual float getBoundingRadius() const { return 1.0f; } const Referential* getReferential() const { return _referential; } diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index 69f9669973..b691bcc8ef 100644 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -64,11 +64,11 @@ void HandData::getLeftRightPalmIndices(int& leftPalmIndex, int& rightPalmIndex) } PalmData::PalmData(HandData* owningHandData) : -_rawRotation(0.f, 0.f, 0.f, 1.f), -_rawPosition(0.f), -_rawVelocity(0.f), -_rotationalVelocity(0.f), -_totalPenetration(0.f), +_rawRotation(0.0f, 0.0f, 0.0f, 1.0f), +_rawPosition(0.0f), +_rawVelocity(0.0f), +_rotationalVelocity(0.0f), +_totalPenetration(0.0f), _controllerButtons(0), _isActive(false), _sixenseID(SIXENSEID_INVALID), diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index 770a44197d..8e44658faf 100644 --- a/libraries/avatars/src/HandData.h +++ b/libraries/avatars/src/HandData.h @@ -100,7 +100,7 @@ public: void addToPosition(const glm::vec3& delta); void addToPenetration(const glm::vec3& penetration) { _totalPenetration += penetration; } - void resolvePenetrations() { addToPosition(-_totalPenetration); _totalPenetration = glm::vec3(0.f); } + void resolvePenetrations() { addToPosition(-_totalPenetration); _totalPenetration = glm::vec3(0.0f); } void setTipPosition(const glm::vec3& position) { _tipPosition = position; } const glm::vec3 getTipPosition() const { return _owningHandData->localToWorldPosition(_tipPosition); } diff --git a/libraries/avatars/src/HeadData.h b/libraries/avatars/src/HeadData.h index 7e27365387..ea7666a6cd 100644 --- a/libraries/avatars/src/HeadData.h +++ b/libraries/avatars/src/HeadData.h @@ -20,12 +20,12 @@ #include // degrees -const float MIN_HEAD_YAW = -180.f; -const float MAX_HEAD_YAW = 180.f; -const float MIN_HEAD_PITCH = -60.f; -const float MAX_HEAD_PITCH = 60.f; -const float MIN_HEAD_ROLL = -50.f; -const float MAX_HEAD_ROLL = 50.f; +const float MIN_HEAD_YAW = -180.0f; +const float MAX_HEAD_YAW = 180.0f; +const float MIN_HEAD_PITCH = -60.0f; +const float MAX_HEAD_PITCH = 60.0f; +const float MIN_HEAD_ROLL = -50.0f; +const float MAX_HEAD_ROLL = 50.0f; class AvatarData; diff --git a/libraries/entities/src/EntityCollisionSystem.cpp b/libraries/entities/src/EntityCollisionSystem.cpp index 448a39de78..a3836bd6d9 100644 --- a/libraries/entities/src/EntityCollisionSystem.cpp +++ b/libraries/entities/src/EntityCollisionSystem.cpp @@ -267,7 +267,7 @@ void EntityCollisionSystem::updateCollisionWithAvatars(EntityItem* entity) { collision->_addedVelocity /= (float)(TREE_SCALE); glm::vec3 relativeVelocity = collision->_addedVelocity - entity->getVelocity(); - if (glm::dot(relativeVelocity, collision->_penetration) <= 0.f) { + if (glm::dot(relativeVelocity, collision->_penetration) <= 0.0f) { // only collide when Entity and collision point are moving toward each other // (doing this prevents some "collision snagging" when Entity penetrates the object) collision->_penetration /= (float)(TREE_SCALE); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index e66da97390..29c4a8b19a 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -44,20 +44,6 @@ EntityItemID EntityScriptingInterface::addEntity(const EntityItemProperties& pro return id; } -EntityItemID EntityScriptingInterface::getEntityItemID(const QString& uuid) { - EntityItemID entityID = EntityItemID(QUuid(uuid), UNKNOWN_ENTITY_TOKEN, false); - - _entityTree->lockForRead(); - EntityItem* entity = const_cast(_entityTree->findEntityByEntityItemID(entityID)); - _entityTree->unlock(); - - if (entity) { - return entity->getEntityItemID(); - } - - return entityID; -} - EntityItemID EntityScriptingInterface::identifyEntity(EntityItemID entityID) { EntityItemID actualID = entityID; diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 7269579ab0..2150fa51da 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -64,9 +64,6 @@ public slots: /// adds a model with the specific properties Q_INVOKABLE EntityItemID addEntity(const EntityItemProperties& properties); - // Get EntityItemID from uuid string - Q_INVOKABLE EntityItemID getEntityItemID(const QString& entityID); - /// identify a recently created model to determine its true ID Q_INVOKABLE EntityItemID identifyEntity(EntityItemID entityID); diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 9d3e46400c..d380cc4f6f 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -623,7 +623,7 @@ void EntityTreeElement::getEntities(const AACube& box, QVector& fou // NOTE: we actually do cube-cube collision queries here, which is sloppy but good enough for now // TODO: decide whether to replace entityCube-cube query with sphere-cube (requires a square root // but will be slightly more accurate). - entityCube.setBox(entity->getPosition() - glm::vec3(radius), 2.f * radius); + entityCube.setBox(entity->getPosition() - glm::vec3(radius), 2.0f * radius); if (entityCube.touches(box)) { foundEntities.push_back(entity); } diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index e0f175d60d..bbf172e31e 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -984,7 +984,7 @@ class JointShapeInfo { public: JointShapeInfo() : numVertices(0), sumVertexWeights(0.0f), sumWeightedRadii(0.0f), numVertexWeights(0), - averageVertex(0.f), boneBegin(0.f), averageRadius(0.f) { + averageVertex(0.0f), boneBegin(0.0f), averageRadius(0.0f) { } // NOTE: the points here are in the "joint frame" which has the "jointEnd" at the origin @@ -1564,7 +1564,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) joint.boneRadius = 0.0f; joint.inverseBindRotation = joint.inverseDefaultRotation; joint.name = model.name; - joint.shapePosition = glm::vec3(0.f); + joint.shapePosition = glm::vec3(0.0f); joint.shapeType = SHAPE_TYPE_UNKNOWN; foreach (const QString& childID, childMap.values(modelID)) { @@ -1890,7 +1890,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } float radiusScale = extractUniformScale(joint.transform * firstFBXCluster.inverseBindMatrix); - glm::vec3 averageVertex(0.f); + glm::vec3 averageVertex(0.0f); foreach (const glm::vec3& vertex, extracted.mesh.vertices) { float proj = glm::dot(boneDirection, boneEnd - vertex); float radiusWeight = (proj < 0.0f || proj > boneLength) ? 0.5f : 1.0f; @@ -1906,7 +1906,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) jointShapeInfo.numVertices = numVertices; if (numVertices > 0) { averageVertex /= (float)jointShapeInfo.numVertices; - float averageRadius = 0.f; + float averageRadius = 0.0f; foreach (const glm::vec3& vertex, extracted.mesh.vertices) { averageRadius += glm::distance(vertex, averageVertex); } @@ -1922,7 +1922,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } // now that all joints have been scanned, compute a collision shape for each joint - glm::vec3 defaultCapsuleAxis(0.f, 1.f, 0.f); + glm::vec3 defaultCapsuleAxis(0.0f, 1.0f, 0.0f); for (int i = 0; i < geometry.joints.size(); ++i) { FBXJoint& joint = geometry.joints[i]; JointShapeInfo& jointShapeInfo = jointShapeInfos[i]; @@ -1955,7 +1955,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) jointShapeInfo.averageVertex /= (float)jointShapeInfo.numVertices; joint.shapePosition = jointShapeInfo.averageVertex; } else { - joint.shapePosition = glm::vec3(0.f); + joint.shapePosition = glm::vec3(0.0f); } if (jointShapeInfo.numVertexWeights == 0 && jointShapeInfo.numVertices > 0) { diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index c06e376a25..fbc7f0d110 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -17,16 +17,10 @@ #include "AttributeRegistry.h" #include "MetavoxelData.h" +#include "Spanner.h" REGISTER_META_OBJECT(FloatAttribute) -REGISTER_META_OBJECT(QRgbAttribute) -REGISTER_META_OBJECT(PackedNormalAttribute) -REGISTER_META_OBJECT(SpannerQRgbAttribute) -REGISTER_META_OBJECT(SpannerPackedNormalAttribute) REGISTER_META_OBJECT(MaterialObject) -REGISTER_META_OBJECT(HeightfieldAttribute) -REGISTER_META_OBJECT(HeightfieldColorAttribute) -REGISTER_META_OBJECT(HeightfieldMaterialAttribute) REGISTER_META_OBJECT(SharedObjectAttribute) REGISTER_META_OBJECT(SharedObjectSetAttribute) REGISTER_META_OBJECT(SpannerSetAttribute) @@ -48,14 +42,6 @@ AttributeRegistry::AttributeRegistry() : _rendererAttribute(registerAttribute(new SharedObjectAttribute("renderer", &MetavoxelRenderer::staticMetaObject, new DefaultMetavoxelRenderer()))), _spannersAttribute(registerAttribute(new SpannerSetAttribute("spanners", &Spanner::staticMetaObject))), - _colorAttribute(registerAttribute(new QRgbAttribute("color"))), - _normalAttribute(registerAttribute(new PackedNormalAttribute("normal"))), - _spannerColorAttribute(registerAttribute(new SpannerQRgbAttribute("spannerColor"))), - _spannerNormalAttribute(registerAttribute(new SpannerPackedNormalAttribute("spannerNormal"))), - _spannerMaskAttribute(registerAttribute(new FloatAttribute("spannerMask"))), - _heightfieldAttribute(registerAttribute(new HeightfieldAttribute("heightfield"))), - _heightfieldColorAttribute(registerAttribute(new HeightfieldColorAttribute("heightfieldColor"))), - _heightfieldMaterialAttribute(registerAttribute(new HeightfieldMaterialAttribute("heightfieldMaterial"))), _voxelColorAttribute(registerAttribute(new VoxelColorAttribute("voxelColor"))), _voxelMaterialAttribute(registerAttribute(new VoxelMaterialAttribute("voxelMaterial"))), _voxelHermiteAttribute(registerAttribute(new VoxelHermiteAttribute("voxelHermite"))) { @@ -63,12 +49,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); - - const float HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER = 32.0f; - _heightfieldAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); - _heightfieldAttribute->setUserFacing(true); - _heightfieldColorAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); - _heightfieldMaterialAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); + _spannersAttribute->setUserFacing(true); const float VOXEL_LOD_THRESHOLD_MULTIPLIER = 16.0f; _voxelColorAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER); @@ -89,8 +70,6 @@ static QScriptValue qDebugFunction(QScriptContext* context, QScriptEngine* engin void AttributeRegistry::configureScriptEngine(QScriptEngine* engine) { QScriptValue registry = engine->newObject(); - registry.setProperty("colorAttribute", engine->newQObject(_colorAttribute.data())); - registry.setProperty("normalAttribute", engine->newQObject(_normalAttribute.data())); registry.setProperty("getAttribute", engine->newFunction(getAttribute, 1)); engine->globalObject().setProperty("AttributeRegistry", registry); engine->globalObject().setProperty("qDebug", engine->newFunction(qDebugFunction, 1)); @@ -292,111 +271,8 @@ MetavoxelNode* Attribute::expandMetavoxelRoot(const MetavoxelNode& root) { return newParent; } -FloatAttribute::FloatAttribute(const QString& name, float defaultValue) : - SimpleInlineAttribute(name, defaultValue) { -} - -QRgbAttribute::QRgbAttribute(const QString& name, QRgb defaultValue) : - InlineAttribute(name, defaultValue) { -} - -bool QRgbAttribute::merge(void*& parent, void* children[], bool postRead) const { - QRgb firstValue = decodeInline(children[0]); - int totalAlpha = qAlpha(firstValue); - int totalRed = qRed(firstValue) * totalAlpha; - int totalGreen = qGreen(firstValue) * totalAlpha; - int totalBlue = qBlue(firstValue) * totalAlpha; - bool allChildrenEqual = true; - for (int i = 1; i < Attribute::MERGE_COUNT; i++) { - QRgb value = decodeInline(children[i]); - int alpha = qAlpha(value); - totalRed += qRed(value) * alpha; - totalGreen += qGreen(value) * alpha; - totalBlue += qBlue(value) * alpha; - totalAlpha += alpha; - allChildrenEqual &= (firstValue == value); - } - if (totalAlpha == 0) { - parent = encodeInline(QRgb()); - } else { - parent = encodeInline(qRgba(totalRed / totalAlpha, totalGreen / totalAlpha, - totalBlue / totalAlpha, totalAlpha / MERGE_COUNT)); - } - return allChildrenEqual; -} - -void* QRgbAttribute::mix(void* first, void* second, float alpha) const { - QRgb firstValue = decodeInline(first); - QRgb secondValue = decodeInline(second); - return encodeInline(qRgba( - glm::mix((float)qRed(firstValue), (float)qRed(secondValue), alpha), - glm::mix((float)qGreen(firstValue), (float)qGreen(secondValue), alpha), - glm::mix((float)qBlue(firstValue), (float)qBlue(secondValue), alpha), - glm::mix((float)qAlpha(firstValue), (float)qAlpha(secondValue), alpha))); -} - -const float EIGHT_BIT_MAXIMUM = 255.0f; - -void* QRgbAttribute::blend(void* source, void* dest) const { - QRgb sourceValue = decodeInline(source); - QRgb destValue = decodeInline(dest); - float alpha = qAlpha(sourceValue) / EIGHT_BIT_MAXIMUM; - return encodeInline(qRgba( - glm::mix((float)qRed(destValue), (float)qRed(sourceValue), alpha), - glm::mix((float)qGreen(destValue), (float)qGreen(sourceValue), alpha), - glm::mix((float)qBlue(destValue), (float)qBlue(sourceValue), alpha), - glm::mix((float)qAlpha(destValue), (float)qAlpha(sourceValue), alpha))); -} - -void* QRgbAttribute::createFromScript(const QScriptValue& value, QScriptEngine* engine) const { - return encodeInline((QRgb)value.toUInt32()); -} - -void* QRgbAttribute::createFromVariant(const QVariant& value) const { - switch (value.userType()) { - case QMetaType::QColor: - return encodeInline(value.value().rgba()); - - default: - return encodeInline((QRgb)value.toUInt()); - } -} - -QWidget* QRgbAttribute::createEditor(QWidget* parent) const { - QColorEditor* editor = new QColorEditor(parent); - editor->setColor(QColor::fromRgba(_defaultValue)); - return editor; -} - -PackedNormalAttribute::PackedNormalAttribute(const QString& name, QRgb defaultValue) : - QRgbAttribute(name, defaultValue) { -} - -bool PackedNormalAttribute::merge(void*& parent, void* children[], bool postRead) const { - QRgb firstValue = decodeInline(children[0]); - glm::vec3 total = unpackNormal(firstValue) * (float)qAlpha(firstValue); - bool allChildrenEqual = true; - for (int i = 1; i < Attribute::MERGE_COUNT; i++) { - QRgb value = decodeInline(children[i]); - total += unpackNormal(value) * (float)qAlpha(value); - allChildrenEqual &= (firstValue == value); - } - float length = glm::length(total); - parent = encodeInline(length < EPSILON ? QRgb() : packNormal(total / length)); - return allChildrenEqual; -} - -void* PackedNormalAttribute::mix(void* first, void* second, float alpha) const { - glm::vec3 firstNormal = unpackNormal(decodeInline(first)); - glm::vec3 secondNormal = unpackNormal(decodeInline(second)); - return encodeInline(packNormal(glm::normalize(glm::mix(firstNormal, secondNormal, alpha)))); -} - -void* PackedNormalAttribute::blend(void* source, void* dest) const { - QRgb sourceValue = decodeInline(source); - QRgb destValue = decodeInline(dest); - float alpha = qAlpha(sourceValue) / EIGHT_BIT_MAXIMUM; - return encodeInline(packNormal(glm::normalize(glm::mix(unpackNormal(destValue), unpackNormal(sourceValue), alpha)))); +FloatAttribute::FloatAttribute(const QString& name) : + SimpleInlineAttribute(name) { } const float CHAR_SCALE = 127.0f; @@ -415,1007 +291,14 @@ glm::vec3 unpackNormal(QRgb value) { (char)qBlue(value) * INVERSE_CHAR_SCALE); } -SpannerQRgbAttribute::SpannerQRgbAttribute(const QString& name, QRgb defaultValue) : - QRgbAttribute(name, defaultValue) { -} - -void SpannerQRgbAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { - value = getDefaultValue(); - in.read(&value, 32); -} - -void SpannerQRgbAttribute::write(Bitstream& out, void* value, bool isLeaf) const { - out.write(&value, 32); -} - -MetavoxelNode* SpannerQRgbAttribute::createMetavoxelNode( - const AttributeValue& value, const MetavoxelNode* original) const { - return new MetavoxelNode(value, original); -} - -bool SpannerQRgbAttribute::merge(void*& parent, void* children[], bool postRead) const { - if (postRead) { - for (int i = 0; i < MERGE_COUNT; i++) { - if (qAlpha(decodeInline(children[i])) != 0) { - return false; - } - } - return true; - } - QRgb parentValue = decodeInline(parent); - int totalAlpha = qAlpha(parentValue) * Attribute::MERGE_COUNT; - int totalRed = qRed(parentValue) * totalAlpha; - int totalGreen = qGreen(parentValue) * totalAlpha; - int totalBlue = qBlue(parentValue) * totalAlpha; - bool allChildrenTransparent = true; - for (int i = 0; i < Attribute::MERGE_COUNT; i++) { - QRgb value = decodeInline(children[i]); - int alpha = qAlpha(value); - totalRed += qRed(value) * alpha; - totalGreen += qGreen(value) * alpha; - totalBlue += qBlue(value) * alpha; - totalAlpha += alpha; - allChildrenTransparent &= (alpha == 0); - } - if (totalAlpha == 0) { - parent = encodeInline(QRgb()); - } else { - parent = encodeInline(qRgba(totalRed / totalAlpha, totalGreen / totalAlpha, - totalBlue / totalAlpha, totalAlpha / MERGE_COUNT)); - } - return allChildrenTransparent; -} - -AttributeValue SpannerQRgbAttribute::inherit(const AttributeValue& parentValue) const { - return AttributeValue(parentValue.getAttribute()); -} - -SpannerPackedNormalAttribute::SpannerPackedNormalAttribute(const QString& name, QRgb defaultValue) : - PackedNormalAttribute(name, defaultValue) { -} - -void SpannerPackedNormalAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { - value = getDefaultValue(); - in.read(&value, 32); -} - -void SpannerPackedNormalAttribute::write(Bitstream& out, void* value, bool isLeaf) const { - out.write(&value, 32); -} - -MetavoxelNode* SpannerPackedNormalAttribute::createMetavoxelNode( - const AttributeValue& value, const MetavoxelNode* original) const { - return new MetavoxelNode(value, original); -} - -bool SpannerPackedNormalAttribute::merge(void*& parent, void* children[], bool postRead) const { - if (postRead) { - for (int i = 0; i < MERGE_COUNT; i++) { - if (qAlpha(decodeInline(children[i])) != 0) { - return false; - } - } - return true; - } - QRgb parentValue = decodeInline(parent); - glm::vec3 total = unpackNormal(parentValue) * (float)(qAlpha(parentValue) * Attribute::MERGE_COUNT); - bool allChildrenTransparent = true; - for (int i = 0; i < Attribute::MERGE_COUNT; i++) { - QRgb value = decodeInline(children[i]); - int alpha = qAlpha(value); - total += unpackNormal(value) * (float)alpha; - allChildrenTransparent &= (alpha == 0); - } - float length = glm::length(total); - parent = encodeInline(length < EPSILON ? QRgb() : packNormal(total / length)); - return allChildrenTransparent; -} - -AttributeValue SpannerPackedNormalAttribute::inherit(const AttributeValue& parentValue) const { - return AttributeValue(parentValue.getAttribute()); -} - DataBlock::~DataBlock() { } -enum HeightfieldImage { NULL_HEIGHTFIELD_IMAGE, NORMAL_HEIGHTFIELD_IMAGE, DEFLATED_HEIGHTFIELD_IMAGE }; - -static QByteArray encodeHeightfieldImage(const QImage& image, bool lossless = false) { - if (image.isNull()) { - return QByteArray(1, NULL_HEIGHTFIELD_IMAGE); - } - QBuffer buffer; - buffer.open(QIODevice::WriteOnly); - const int JPEG_ENCODE_THRESHOLD = 16; - if (image.width() >= JPEG_ENCODE_THRESHOLD && image.height() >= JPEG_ENCODE_THRESHOLD && !lossless) { - qint32 offsetX = image.offset().x(), offsetY = image.offset().y(); - buffer.write((char*)&offsetX, sizeof(qint32)); - buffer.write((char*)&offsetY, sizeof(qint32)); - image.save(&buffer, "JPG"); - return QByteArray(1, DEFLATED_HEIGHTFIELD_IMAGE) + qCompress(buffer.data()); - - } else { - buffer.putChar(NORMAL_HEIGHTFIELD_IMAGE); - image.save(&buffer, "PNG"); - return buffer.data(); - } -} - -const QImage decodeHeightfieldImage(const QByteArray& data) { - switch (data.at(0)) { - case NULL_HEIGHTFIELD_IMAGE: - default: - return QImage(); - - case NORMAL_HEIGHTFIELD_IMAGE: - return QImage::fromData(QByteArray::fromRawData(data.constData() + 1, data.size() - 1)); - - case DEFLATED_HEIGHTFIELD_IMAGE: { - QByteArray inflated = qUncompress((const uchar*)data.constData() + 1, data.size() - 1); - const int OFFSET_SIZE = sizeof(qint32) * 2; - QImage image = QImage::fromData((const uchar*)inflated.constData() + OFFSET_SIZE, inflated.size() - OFFSET_SIZE); - const qint32* offsets = (const qint32*)inflated.constData(); - image.setOffset(QPoint(offsets[0], offsets[1])); - return image; - } - } -} - -HeightfieldHeightData::HeightfieldHeightData(const QByteArray& contents) : - _contents(contents) { -} - -HeightfieldHeightData::HeightfieldHeightData(Bitstream& in, int bytes) { - read(in, bytes); -} - -HeightfieldHeightData::HeightfieldHeightData(Bitstream& in, int bytes, const HeightfieldHeightDataPointer& reference) { - if (!reference) { - read(in, bytes); - return; - } - QMutexLocker locker(&reference->getEncodedDeltaMutex()); - reference->setEncodedDelta(in.readAligned(bytes)); - reference->setDeltaData(DataBlockPointer(this)); - _contents = reference->getContents(); - QImage image = decodeHeightfieldImage(reference->getEncodedDelta()); - if (image.isNull()) { - return; - } - QPoint offset = image.offset(); - image = image.convertToFormat(QImage::Format_RGB888); - if (offset.x() == 0) { - set(image); - return; - } - int minX = offset.x() - 1; - int minY = offset.y() - 1; - int size = glm::sqrt((float)_contents.size()); - char* lineDest = _contents.data() + minY * size + minX; - for (int y = 0; y < image.height(); y++) { - const uchar* src = image.constScanLine(y); - for (char* dest = lineDest, *end = dest + image.width(); dest != end; dest++, src += COLOR_BYTES) { - *dest = *src; - } - lineDest += size; - } -} - -HeightfieldHeightData::HeightfieldHeightData(Bitstream& in, int bytes, const HeightfieldHeightDataPointer& ancestor, - const glm::vec3& minimum, float size) { - QMutexLocker locker(&_encodedSubdivisionsMutex); - int index = (int)glm::round(glm::log(size) / glm::log(0.5f)) - 1; - if (_encodedSubdivisions.size() <= index) { - _encodedSubdivisions.resize(index + 1); - } - EncodedSubdivision& subdivision = _encodedSubdivisions[index]; - subdivision.data = in.readAligned(bytes); - subdivision.ancestor = ancestor; - QImage image = decodeHeightfieldImage(subdivision.data); - if (image.isNull()) { - return; - } - image = image.convertToFormat(QImage::Format_RGB888); - int destSize = image.width(); - const uchar* src = image.constBits(); - const QByteArray& ancestorContents = ancestor->getContents(); - - int ancestorSize = glm::sqrt((float)ancestorContents.size()); - float ancestorY = minimum.z * ancestorSize; - float ancestorIncrement = size * ancestorSize / destSize; - - _contents = QByteArray(destSize * destSize, 0); - char* dest = _contents.data(); - - for (int y = 0; y < destSize; y++, ancestorY += ancestorIncrement) { - const uchar* lineRef = (const uchar*)ancestorContents.constData() + (int)ancestorY * ancestorSize; - float ancestorX = minimum.x * ancestorSize; - for (char* end = dest + destSize; dest != end; src += COLOR_BYTES, ancestorX += ancestorIncrement) { - const uchar* ref = lineRef + (int)ancestorX; - *dest++ = *ref++ + *src; - } - } -} - -void HeightfieldHeightData::write(Bitstream& out) { - QMutexLocker locker(&_encodedMutex); - if (_encoded.isEmpty()) { - QImage image; - int size = glm::sqrt((float)_contents.size()); - image = QImage(size, size, QImage::Format_RGB888); - uchar* dest = image.bits(); - for (const char* src = _contents.constData(), *end = src + _contents.size(); src != end; src++) { - *dest++ = *src; - *dest++ = *src; - *dest++ = *src; - } - _encoded = encodeHeightfieldImage(image); - } - out << _encoded.size(); - out.writeAligned(_encoded); -} - -void HeightfieldHeightData::writeDelta(Bitstream& out, const HeightfieldHeightDataPointer& reference) { - if (!reference || reference->getContents().size() != _contents.size()) { - write(out); - return; - } - QMutexLocker locker(&reference->getEncodedDeltaMutex()); - if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { - QImage image; - int size = glm::sqrt((float)_contents.size()); - int minX = size, minY = size; - int maxX = -1, maxY = -1; - const char* src = _contents.constData(); - const char* ref = reference->getContents().constData(); - for (int y = 0; y < size; y++) { - bool difference = false; - for (int x = 0; x < size; x++) { - if (*src++ != *ref++) { - minX = qMin(minX, x); - maxX = qMax(maxX, x); - difference = true; - } - } - if (difference) { - minY = qMin(minY, y); - maxY = qMax(maxY, y); - } - } - if (maxX >= minX) { - int width = qMax(maxX - minX + 1, 0); - int height = qMax(maxY - minY + 1, 0); - image = QImage(width, height, QImage::Format_RGB888); - const uchar* lineSrc = (const uchar*)_contents.constData() + minY * size + minX; - for (int y = 0; y < height; y++) { - uchar* dest = image.scanLine(y); - for (const uchar* src = lineSrc, *end = src + width; src != end; src++) { - *dest++ = *src; - *dest++ = *src; - *dest++ = *src; - } - lineSrc += size; - } - } - image.setOffset(QPoint(minX + 1, minY + 1)); - reference->setEncodedDelta(encodeHeightfieldImage(image)); - reference->setDeltaData(DataBlockPointer(this)); - } - out << reference->getEncodedDelta().size(); - out.writeAligned(reference->getEncodedDelta()); -} - -void HeightfieldHeightData::writeSubdivided(Bitstream& out, const HeightfieldHeightDataPointer& ancestor, - const glm::vec3& minimum, float size) { - QMutexLocker locker(&_encodedSubdivisionsMutex); - int index = (int)glm::round(glm::log(size) / glm::log(0.5f)) - 1; - if (_encodedSubdivisions.size() <= index) { - _encodedSubdivisions.resize(index + 1); - } - EncodedSubdivision& subdivision = _encodedSubdivisions[index]; - if (subdivision.data.isEmpty() || subdivision.ancestor != ancestor) { - QImage image; - const QByteArray& ancestorContents = ancestor->getContents(); - const uchar* src = (const uchar*)_contents.constData(); - - int destSize = glm::sqrt((float)_contents.size()); - image = QImage(destSize, destSize, QImage::Format_RGB888); - uchar* dest = image.bits(); - - int ancestorSize = glm::sqrt((float)ancestorContents.size()); - float ancestorY = minimum.z * ancestorSize; - float ancestorIncrement = size * ancestorSize / destSize; - - for (int y = 0; y < destSize; y++, ancestorY += ancestorIncrement) { - const uchar* lineRef = (const uchar*)ancestorContents.constData() + (int)ancestorY * ancestorSize; - float ancestorX = minimum.x * ancestorSize; - for (const uchar* end = src + destSize; src != end; ancestorX += ancestorIncrement) { - const uchar* ref = lineRef + (int)ancestorX; - uchar difference = *src++ - *ref; - *dest++ = difference; - *dest++ = difference; - *dest++ = difference; - } - } - subdivision.data = encodeHeightfieldImage(image, true); - subdivision.ancestor = ancestor; - } - out << subdivision.data.size(); - out.writeAligned(subdivision.data); -} - -void HeightfieldHeightData::read(Bitstream& in, int bytes) { - set(decodeHeightfieldImage(_encoded = in.readAligned(bytes)).convertToFormat(QImage::Format_RGB888)); -} - -void HeightfieldHeightData::set(const QImage& image) { - _contents.resize(image.width() * image.height()); - char* dest = _contents.data(); - for (const uchar* src = image.constBits(), *end = src + _contents.size() * COLOR_BYTES; - src != end; src += COLOR_BYTES) { - *dest++ = *src; - } -} - -HeightfieldColorData::HeightfieldColorData(const QByteArray& contents) : - _contents(contents) { -} - -HeightfieldColorData::HeightfieldColorData(Bitstream& in, int bytes) { - read(in, bytes); -} - -HeightfieldColorData::HeightfieldColorData(Bitstream& in, int bytes, const HeightfieldColorDataPointer& reference) { - if (!reference) { - read(in, bytes); - return; - } - QMutexLocker locker(&reference->getEncodedDeltaMutex()); - reference->setEncodedDelta(in.readAligned(bytes)); - reference->setDeltaData(DataBlockPointer(this)); - _contents = reference->getContents(); - QImage image = decodeHeightfieldImage(reference->getEncodedDelta()); - if (image.isNull()) { - return; - } - QPoint offset = image.offset(); - image = image.convertToFormat(QImage::Format_RGB888); - if (offset.x() == 0) { - set(image); - return; - } - int minX = offset.x() - 1; - int minY = offset.y() - 1; - int size = glm::sqrt(_contents.size() / (float)COLOR_BYTES); - char* dest = _contents.data() + (minY * size + minX) * COLOR_BYTES; - int destStride = size * COLOR_BYTES; - int srcStride = image.width() * COLOR_BYTES; - for (int y = 0; y < image.height(); y++) { - memcpy(dest, image.constScanLine(y), srcStride); - dest += destStride; - } -} - -HeightfieldColorData::HeightfieldColorData(Bitstream& in, int bytes, const HeightfieldColorDataPointer& ancestor, - const glm::vec3& minimum, float size) { - QMutexLocker locker(&_encodedSubdivisionsMutex); - int index = (int)glm::round(glm::log(size) / glm::log(0.5f)) - 1; - if (_encodedSubdivisions.size() <= index) { - _encodedSubdivisions.resize(index + 1); - } - EncodedSubdivision& subdivision = _encodedSubdivisions[index]; - subdivision.data = in.readAligned(bytes); - subdivision.ancestor = ancestor; - QImage image = decodeHeightfieldImage(subdivision.data); - if (image.isNull()) { - return; - } - image = image.convertToFormat(QImage::Format_RGB888); - int destSize = image.width(); - const uchar* src = image.constBits(); - const QByteArray& ancestorContents = ancestor->getContents(); - - int ancestorSize = glm::sqrt(ancestorContents.size() / (float)COLOR_BYTES); - float ancestorY = minimum.z * ancestorSize; - float ancestorIncrement = size * ancestorSize / destSize; - int ancestorStride = ancestorSize * COLOR_BYTES; - - _contents = QByteArray(destSize * destSize * COLOR_BYTES, 0); - char* dest = _contents.data(); - int stride = image.width() * COLOR_BYTES; - - for (int y = 0; y < destSize; y++, ancestorY += ancestorIncrement) { - const uchar* lineRef = (const uchar*)ancestorContents.constData() + (int)ancestorY * ancestorStride; - float ancestorX = minimum.x * ancestorSize; - for (char* end = dest + stride; dest != end; ancestorX += ancestorIncrement) { - const uchar* ref = lineRef + (int)ancestorX * COLOR_BYTES; - *dest++ = *ref++ + *src++; - *dest++ = *ref++ + *src++; - *dest++ = *ref++ + *src++; - } - } -} - -void HeightfieldColorData::write(Bitstream& out) { - QMutexLocker locker(&_encodedMutex); - if (_encoded.isEmpty()) { - QImage image; - int size = glm::sqrt(_contents.size() / (float)COLOR_BYTES); - image = QImage((uchar*)_contents.data(), size, size, QImage::Format_RGB888); - _encoded = encodeHeightfieldImage(image); - } - out << _encoded.size(); - out.writeAligned(_encoded); -} - -void HeightfieldColorData::writeDelta(Bitstream& out, const HeightfieldColorDataPointer& reference) { - if (!reference || reference->getContents().size() != _contents.size()) { - write(out); - return; - } - QMutexLocker locker(&reference->getEncodedDeltaMutex()); - if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { - QImage image; - int size = glm::sqrt(_contents.size() / (float)COLOR_BYTES); - int minX = size, minY = size; - int maxX = -1, maxY = -1; - const char* src = _contents.constData(); - const char* ref = reference->getContents().constData(); - for (int y = 0; y < size; y++) { - bool difference = false; - for (int x = 0; x < size; x++, src += COLOR_BYTES, ref += COLOR_BYTES) { - if (src[0] != ref[0] || src[1] != ref[1] || src[2] != ref[2]) { - minX = qMin(minX, x); - maxX = qMax(maxX, x); - difference = true; - } - } - if (difference) { - minY = qMin(minY, y); - maxY = qMax(maxY, y); - } - } - if (maxX >= minX) { - int width = maxX - minX + 1; - int height = maxY - minY + 1; - image = QImage(width, height, QImage::Format_RGB888); - src = _contents.constData() + (minY * size + minX) * COLOR_BYTES; - int srcStride = size * COLOR_BYTES; - int destStride = width * COLOR_BYTES; - for (int y = 0; y < height; y++) { - memcpy(image.scanLine(y), src, destStride); - src += srcStride; - } - } - image.setOffset(QPoint(minX + 1, minY + 1)); - reference->setEncodedDelta(encodeHeightfieldImage(image)); - reference->setDeltaData(DataBlockPointer(this)); - } - out << reference->getEncodedDelta().size(); - out.writeAligned(reference->getEncodedDelta()); -} - -void HeightfieldColorData::writeSubdivided(Bitstream& out, const HeightfieldColorDataPointer& ancestor, - const glm::vec3& minimum, float size) { - QMutexLocker locker(&_encodedSubdivisionsMutex); - int index = (int)glm::round(glm::log(size) / glm::log(0.5f)) - 1; - if (_encodedSubdivisions.size() <= index) { - _encodedSubdivisions.resize(index + 1); - } - EncodedSubdivision& subdivision = _encodedSubdivisions[index]; - if (subdivision.data.isEmpty() || subdivision.ancestor != ancestor) { - QImage image; - const QByteArray& ancestorContents = ancestor->getContents(); - const uchar* src = (const uchar*)_contents.constData(); - - int destSize = glm::sqrt(_contents.size() / (float)COLOR_BYTES); - image = QImage(destSize, destSize, QImage::Format_RGB888); - uchar* dest = image.bits(); - int stride = destSize * COLOR_BYTES; - - int ancestorSize = glm::sqrt(ancestorContents.size() / (float)COLOR_BYTES); - float ancestorY = minimum.z * ancestorSize; - float ancestorIncrement = size * ancestorSize / destSize; - int ancestorStride = ancestorSize * COLOR_BYTES; - - for (int y = 0; y < destSize; y++, ancestorY += ancestorIncrement) { - const uchar* lineRef = (const uchar*)ancestorContents.constData() + (int)ancestorY * ancestorStride; - float ancestorX = minimum.x * ancestorSize; - for (const uchar* end = src + stride; src != end; ancestorX += ancestorIncrement) { - const uchar* ref = lineRef + (int)ancestorX * COLOR_BYTES; - *dest++ = *src++ - *ref++; - *dest++ = *src++ - *ref++; - *dest++ = *src++ - *ref++; - } - } - subdivision.data = encodeHeightfieldImage(image, true); - subdivision.ancestor = ancestor; - } - out << subdivision.data.size(); - out.writeAligned(subdivision.data); -} - -void HeightfieldColorData::read(Bitstream& in, int bytes) { - set(decodeHeightfieldImage(_encoded = in.readAligned(bytes)).convertToFormat(QImage::Format_RGB888)); -} - -void HeightfieldColorData::set(const QImage& image) { - _contents.resize(image.width() * image.height() * COLOR_BYTES); - memcpy(_contents.data(), image.constBits(), _contents.size()); -} - -const int HEIGHTFIELD_MATERIAL_HEADER_SIZE = sizeof(qint32) * 4; - -static QByteArray encodeHeightfieldMaterial(int offsetX, int offsetY, int width, int height, const QByteArray& contents) { - QByteArray inflated(HEIGHTFIELD_MATERIAL_HEADER_SIZE, 0); - qint32* header = (qint32*)inflated.data(); - *header++ = offsetX; - *header++ = offsetY; - *header++ = width; - *header++ = height; - inflated.append(contents); - return qCompress(inflated); -} - -static QByteArray decodeHeightfieldMaterial(const QByteArray& encoded, int& offsetX, int& offsetY, int& width, int& height) { - QByteArray inflated = qUncompress(encoded); - const qint32* header = (const qint32*)inflated.constData(); - offsetX = *header++; - offsetY = *header++; - width = *header++; - height = *header++; - return inflated.mid(HEIGHTFIELD_MATERIAL_HEADER_SIZE); -} - -HeightfieldMaterialData::HeightfieldMaterialData(const QByteArray& contents, const QVector& materials) : - _contents(contents), - _materials(materials) { -} - -HeightfieldMaterialData::HeightfieldMaterialData(Bitstream& in, int bytes) { - read(in, bytes); -} - -HeightfieldMaterialData::HeightfieldMaterialData(Bitstream& in, int bytes, const HeightfieldMaterialDataPointer& reference) { - if (!reference) { - read(in, bytes); - return; - } - QMutexLocker locker(&reference->getEncodedDeltaMutex()); - reference->setEncodedDelta(in.readAligned(bytes)); - in.readDelta(_materials, reference->getMaterials()); - reference->setDeltaData(DataBlockPointer(this)); - _contents = reference->getContents(); - - int offsetX, offsetY, width, height; - QByteArray delta = decodeHeightfieldMaterial(reference->getEncodedDelta(), offsetX, offsetY, width, height); - if (delta.isEmpty()) { - return; - } - if (offsetX == 0) { - _contents = delta; - return; - } - int minX = offsetX - 1; - int minY = offsetY - 1; - int size = glm::sqrt((float)_contents.size()); - const char* src = delta.constData(); - char* dest = _contents.data() + minY * size + minX; - for (int y = 0; y < height; y++, src += width, dest += size) { - memcpy(dest, src, width); - } -} - -void HeightfieldMaterialData::write(Bitstream& out) { - QMutexLocker locker(&_encodedMutex); - if (_encoded.isEmpty()) { - int size = glm::sqrt((float)_contents.size()); - _encoded = encodeHeightfieldMaterial(0, 0, size, size, _contents); - } - out << _encoded.size(); - out.writeAligned(_encoded); - out << _materials; -} - -void HeightfieldMaterialData::writeDelta(Bitstream& out, const HeightfieldMaterialDataPointer& reference) { - if (!reference || reference->getContents().size() != _contents.size()) { - write(out); - return; - } - QMutexLocker locker(&reference->getEncodedDeltaMutex()); - if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { - int size = glm::sqrt((float)_contents.size()); - int minX = size, minY = size; - int maxX = -1, maxY = -1; - const char* src = _contents.constData(); - const char* ref = reference->getContents().constData(); - for (int y = 0; y < size; y++) { - bool difference = false; - for (int x = 0; x < size; x++) { - if (*src++ != *ref++) { - minX = qMin(minX, x); - maxX = qMax(maxX, x); - difference = true; - } - } - if (difference) { - minY = qMin(minY, y); - maxY = qMax(maxY, y); - } - } - QByteArray delta; - int width = 0, height = 0; - if (maxX >= minX) { - width = maxX - minX + 1; - height = maxY - minY + 1; - delta = QByteArray(width * height, 0); - char* dest = delta.data(); - src = _contents.constData() + minY * size + minX; - for (int y = 0; y < height; y++, src += size, dest += width) { - memcpy(dest, src, width); - } - } - reference->setEncodedDelta(encodeHeightfieldMaterial(minX + 1, minY + 1, width, height, delta)); - reference->setDeltaData(DataBlockPointer(this)); - } - out << reference->getEncodedDelta().size(); - out.writeAligned(reference->getEncodedDelta()); - out.writeDelta(_materials, reference->getMaterials()); -} - -void HeightfieldMaterialData::read(Bitstream& in, int bytes) { - int offsetX, offsetY, width, height; - _contents = decodeHeightfieldMaterial(_encoded = in.readAligned(bytes), offsetX, offsetY, width, height); - in >> _materials; -} - MaterialObject::MaterialObject() : _scaleS(1.0f), _scaleT(1.0f) { } -HeightfieldAttribute::HeightfieldAttribute(const QString& name) : - InlineAttribute(name) { -} - -void HeightfieldAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { - if (!isLeaf) { - return; - } - int size; - in >> size; - if (size == 0) { - *(HeightfieldHeightDataPointer*)&value = HeightfieldHeightDataPointer(); - } else { - *(HeightfieldHeightDataPointer*)&value = HeightfieldHeightDataPointer(new HeightfieldHeightData(in, size)); - } -} - -void HeightfieldAttribute::write(Bitstream& out, void* value, bool isLeaf) const { - if (!isLeaf) { - return; - } - HeightfieldHeightDataPointer data = decodeInline(value); - if (data) { - data->write(out); - } else { - out << 0; - } -} - -void HeightfieldAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { - if (!isLeaf) { - return; - } - int size; - in >> size; - if (size == 0) { - *(HeightfieldHeightDataPointer*)&value = HeightfieldHeightDataPointer(); - } else { - *(HeightfieldHeightDataPointer*)&value = HeightfieldHeightDataPointer(new HeightfieldHeightData( - in, size, decodeInline(reference))); - } -} - -void HeightfieldAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { - if (!isLeaf) { - return; - } - HeightfieldHeightDataPointer data = decodeInline(value); - if (data) { - data->writeDelta(out, decodeInline(reference)); - } else { - out << 0; - } -} - -bool HeightfieldAttribute::merge(void*& parent, void* children[], bool postRead) const { - int maxSize = 0; - for (int i = 0; i < MERGE_COUNT; i++) { - HeightfieldHeightDataPointer pointer = decodeInline(children[i]); - if (pointer) { - maxSize = qMax(maxSize, pointer->getContents().size()); - } - } - if (maxSize == 0) { - *(HeightfieldHeightDataPointer*)&parent = HeightfieldHeightDataPointer(); - return true; - } - int size = glm::sqrt((float)maxSize); - QByteArray contents(size * size, 0); - int halfSize = size / 2; - for (int i = 0; i < MERGE_COUNT; i++) { - HeightfieldHeightDataPointer child = decodeInline(children[i]); - if (!child) { - continue; - } - const QByteArray& childContents = child->getContents(); - int childSize = glm::sqrt((float)childContents.size()); - const int INDEX_MASK = 1; - int xIndex = i & INDEX_MASK; - const int Y_SHIFT = 1; - int yIndex = (i >> Y_SHIFT) & INDEX_MASK; - if (yIndex == 0 && decodeInline(children[i | (1 << Y_SHIFT)])) { - continue; // bottom is overriden by top - } - const int HALF_RANGE = 128; - int yOffset = yIndex * HALF_RANGE; - int Z_SHIFT = 2; - int zIndex = (i >> Z_SHIFT) & INDEX_MASK; - char* dest = contents.data() + (zIndex * halfSize * size) + (xIndex * halfSize); - uchar* src = (uchar*)childContents.data(); - int childSizePlusOne = childSize + 1; - if (childSize == size) { - // simple case: one destination value for four child values - for (int z = 0; z < halfSize; z++) { - for (char* end = dest + halfSize; dest != end; src += 2) { - int max = qMax(qMax(src[0], src[1]), qMax(src[childSize], src[childSizePlusOne])); - *dest++ = (max == 0) ? 0 : (yOffset + (max >> 1)); - } - dest += halfSize; - src += childSize; - } - } else { - // more complex: N destination values for four child values - int halfChildSize = childSize / 2; - int destPerSrc = size / childSize; - for (int z = 0; z < halfChildSize; z++) { - for (uchar* end = src + childSize; src != end; src += 2) { - int max = qMax(qMax(src[0], src[1]), qMax(src[childSize], src[childSizePlusOne])); - memset(dest, (max == 0) ? 0 : (yOffset + (max >> 1)), destPerSrc); - dest += destPerSrc; - } - dest += halfSize; - for (int j = 1; j < destPerSrc; j++) { - memcpy(dest, dest - size, halfSize); - dest += size; - } - src += childSize; - } - } - } - *(HeightfieldHeightDataPointer*)&parent = HeightfieldHeightDataPointer(new HeightfieldHeightData(contents)); - return false; -} - -AttributeValue HeightfieldAttribute::inherit(const AttributeValue& parentValue) const { - return AttributeValue(parentValue.getAttribute()); -} - -HeightfieldColorAttribute::HeightfieldColorAttribute(const QString& name) : - InlineAttribute(name) { -} - -void HeightfieldColorAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { - if (!isLeaf) { - return; - } - int size; - in >> size; - if (size == 0) { - *(HeightfieldColorDataPointer*)&value = HeightfieldColorDataPointer(); - } else { - *(HeightfieldColorDataPointer*)&value = HeightfieldColorDataPointer(new HeightfieldColorData(in, size)); - } -} - -void HeightfieldColorAttribute::write(Bitstream& out, void* value, bool isLeaf) const { - if (!isLeaf) { - return; - } - HeightfieldColorDataPointer data = decodeInline(value); - if (data) { - data->write(out); - } else { - out << 0; - } -} - -void HeightfieldColorAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { - if (!isLeaf) { - return; - } - int size; - in >> size; - if (size == 0) { - *(HeightfieldColorDataPointer*)&value = HeightfieldColorDataPointer(); - } else { - *(HeightfieldColorDataPointer*)&value = HeightfieldColorDataPointer(new HeightfieldColorData( - in, size, decodeInline(reference))); - } -} - -void HeightfieldColorAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { - if (!isLeaf) { - return; - } - HeightfieldColorDataPointer data = decodeInline(value); - if (data) { - data->writeDelta(out, decodeInline(reference)); - } else { - out << 0; - } -} - -bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool postRead) const { - int maxSize = 0; - for (int i = 0; i < MERGE_COUNT; i++) { - HeightfieldColorDataPointer pointer = decodeInline(children[i]); - if (pointer) { - maxSize = qMax(maxSize, pointer->getContents().size()); - } - } - if (maxSize == 0) { - *(HeightfieldColorDataPointer*)&parent = HeightfieldColorDataPointer(); - return true; - } - int size = glm::sqrt(maxSize / (float)DataBlock::COLOR_BYTES); - QByteArray contents(size * size * DataBlock::COLOR_BYTES, 0); - int halfSize = size / 2; - for (int i = 0; i < MERGE_COUNT; i++) { - HeightfieldColorDataPointer child = decodeInline(children[i]); - if (!child) { - continue; - } - const QByteArray& childContents = child->getContents(); - int childSize = glm::sqrt(childContents.size() / (float)DataBlock::COLOR_BYTES); - const int INDEX_MASK = 1; - int xIndex = i & INDEX_MASK; - const int Y_SHIFT = 1; - int yIndex = (i >> Y_SHIFT) & INDEX_MASK; - if (yIndex == 0 && decodeInline(children[i | (1 << Y_SHIFT)])) { - continue; // bottom is overriden by top - } - int Z_SHIFT = 2; - int zIndex = (i >> Z_SHIFT) & INDEX_MASK; - char* dest = contents.data() + ((zIndex * halfSize * size) + (xIndex * halfSize)) * DataBlock::COLOR_BYTES; - uchar* src = (uchar*)childContents.data(); - int childStride = childSize * DataBlock::COLOR_BYTES; - int stride = size * DataBlock::COLOR_BYTES; - int halfStride = stride / 2; - int childStep = 2 * DataBlock::COLOR_BYTES; - int redOffset3 = childStride + DataBlock::COLOR_BYTES; - int greenOffset1 = DataBlock::COLOR_BYTES + 1; - int greenOffset2 = childStride + 1; - int greenOffset3 = childStride + DataBlock::COLOR_BYTES + 1; - int blueOffset1 = DataBlock::COLOR_BYTES + 2; - int blueOffset2 = childStride + 2; - int blueOffset3 = childStride + DataBlock::COLOR_BYTES + 2; - if (childSize == size) { - // simple case: one destination value for four child values - for (int z = 0; z < halfSize; z++) { - for (char* end = dest + halfSize * DataBlock::COLOR_BYTES; dest != end; src += childStep) { - *dest++ = ((int)src[0] + (int)src[DataBlock::COLOR_BYTES] + - (int)src[childStride] + (int)src[redOffset3]) >> 2; - *dest++ = ((int)src[1] + (int)src[greenOffset1] + (int)src[greenOffset2] + (int)src[greenOffset3]) >> 2; - *dest++ = ((int)src[2] + (int)src[blueOffset1] + (int)src[blueOffset2] + (int)src[blueOffset3]) >> 2; - } - dest += halfStride; - src += childStride; - } - } else { - // more complex: N destination values for four child values - int halfChildSize = childSize / 2; - int destPerSrc = size / childSize; - for (int z = 0; z < halfChildSize; z++) { - for (uchar* end = src + childSize * DataBlock::COLOR_BYTES; src != end; src += childStep) { - *dest++ = ((int)src[0] + (int)src[DataBlock::COLOR_BYTES] + - (int)src[childStride] + (int)src[redOffset3]) >> 2; - *dest++ = ((int)src[1] + (int)src[greenOffset1] + (int)src[greenOffset2] + (int)src[greenOffset3]) >> 2; - *dest++ = ((int)src[2] + (int)src[blueOffset1] + (int)src[blueOffset2] + (int)src[blueOffset3]) >> 2; - for (int j = 1; j < destPerSrc; j++) { - memcpy(dest, dest - DataBlock::COLOR_BYTES, DataBlock::COLOR_BYTES); - dest += DataBlock::COLOR_BYTES; - } - } - dest += halfStride; - for (int j = 1; j < destPerSrc; j++) { - memcpy(dest, dest - stride, halfStride); - dest += stride; - } - src += childStride; - } - } - } - *(HeightfieldColorDataPointer*)&parent = HeightfieldColorDataPointer(new HeightfieldColorData(contents)); - return false; -} - -AttributeValue HeightfieldColorAttribute::inherit(const AttributeValue& parentValue) const { - return AttributeValue(parentValue.getAttribute()); -} - -HeightfieldMaterialAttribute::HeightfieldMaterialAttribute(const QString& name) : - InlineAttribute(name) { -} - -void HeightfieldMaterialAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { - if (!isLeaf) { - return; - } - int size; - in >> size; - if (size == 0) { - *(HeightfieldMaterialDataPointer*)&value = HeightfieldMaterialDataPointer(); - } else { - *(HeightfieldMaterialDataPointer*)&value = HeightfieldMaterialDataPointer(new HeightfieldMaterialData(in, size)); - } -} - -void HeightfieldMaterialAttribute::write(Bitstream& out, void* value, bool isLeaf) const { - if (!isLeaf) { - return; - } - HeightfieldMaterialDataPointer data = decodeInline(value); - if (data) { - data->write(out); - } else { - out << 0; - } -} - -void HeightfieldMaterialAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { - if (!isLeaf) { - return; - } - int size; - in >> size; - if (size == 0) { - *(HeightfieldMaterialDataPointer*)&value = HeightfieldMaterialDataPointer(); - } else { - *(HeightfieldMaterialDataPointer*)&value = HeightfieldMaterialDataPointer(new HeightfieldMaterialData( - in, size, decodeInline(reference))); - } -} - -void HeightfieldMaterialAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { - if (!isLeaf) { - return; - } - HeightfieldMaterialDataPointer data = decodeInline(value); - if (data) { - data->writeDelta(out, decodeInline(reference)); - } else { - out << 0; - } -} - -bool HeightfieldMaterialAttribute::merge(void*& parent, void* children[], bool postRead) const { - int maxSize = 0; - for (int i = 0; i < MERGE_COUNT; i++) { - HeightfieldMaterialDataPointer pointer = decodeInline(children[i]); - if (pointer) { - maxSize = qMax(maxSize, pointer->getContents().size()); - } - } - *(HeightfieldMaterialDataPointer*)&parent = HeightfieldMaterialDataPointer(); - return maxSize == 0; -} - -AttributeValue HeightfieldMaterialAttribute::inherit(const AttributeValue& parentValue) const { - return AttributeValue(parentValue.getAttribute()); -} - static QHash countIndices(const QByteArray& contents) { QHash counts; for (const uchar* src = (const uchar*)contents.constData(), *end = src + contents.size(); src != end; src++) { @@ -1426,6 +309,8 @@ static QHash countIndices(const QByteArray& contents) { return counts; } +const float EIGHT_BIT_MAXIMUM = 255.0f; + uchar getMaterialIndex(const SharedObjectPointer& material, QVector& materials, QByteArray& contents) { if (!(material && static_cast(material.data())->getDiffuse().isValid())) { return 0; diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 945c4df94b..50814ac912 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -88,21 +88,6 @@ public: /// Returns a reference to the standard SharedObjectSet "spanners" attribute. const AttributePointer& getSpannersAttribute() const { return _spannersAttribute; } - /// Returns a reference to the standard QRgb "color" attribute. - const AttributePointer& getColorAttribute() const { return _colorAttribute; } - - /// Returns a reference to the standard packed normal "normal" attribute. - const AttributePointer& getNormalAttribute() const { return _normalAttribute; } - - /// Returns a reference to the standard QRgb "spannerColor" attribute. - const AttributePointer& getSpannerColorAttribute() const { return _spannerColorAttribute; } - - /// Returns a reference to the standard packed normal "spannerNormal" attribute. - const AttributePointer& getSpannerNormalAttribute() const { return _spannerNormalAttribute; } - - /// Returns a reference to the standard "spannerMask" attribute. - const AttributePointer& getSpannerMaskAttribute() const { return _spannerMaskAttribute; } - /// Returns a reference to the standard HeightfieldHeightDataPointer "heightfield" attribute. const AttributePointer& getHeightfieldAttribute() const { return _heightfieldAttribute; } @@ -131,11 +116,6 @@ private: AttributePointer _guideAttribute; AttributePointer _rendererAttribute; AttributePointer _spannersAttribute; - AttributePointer _colorAttribute; - AttributePointer _normalAttribute; - AttributePointer _spannerColorAttribute; - AttributePointer _spannerNormalAttribute; - AttributePointer _spannerMaskAttribute; AttributePointer _heightfieldAttribute; AttributePointer _heightfieldColorAttribute; AttributePointer _heightfieldMaterialAttribute; @@ -366,51 +346,13 @@ template inline bool SimpleInlineAttribute::merge( return allChildrenEqual; } -/// Simple float attribute. +/// A simple float attribute. class FloatAttribute : public SimpleInlineAttribute { Q_OBJECT - Q_PROPERTY(float defaultValue MEMBER _defaultValue) - + public: - Q_INVOKABLE FloatAttribute(const QString& name = QString(), float defaultValue = 0.0f); -}; - -/// Provides appropriate averaging for RGBA values. -class QRgbAttribute : public InlineAttribute { - Q_OBJECT - Q_PROPERTY(uint defaultValue MEMBER _defaultValue) - -public: - - Q_INVOKABLE QRgbAttribute(const QString& name = QString(), QRgb defaultValue = QRgb()); - - virtual bool merge(void*& parent, void* children[], bool postRead = false) const; - - virtual void* mix(void* first, void* second, float alpha) const; - - virtual void* blend(void* source, void* dest) const; - - virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const; - - virtual void* createFromVariant(const QVariant& value) const; - - virtual QWidget* createEditor(QWidget* parent = NULL) const; -}; - -/// Provides appropriate averaging for packed normals. -class PackedNormalAttribute : public QRgbAttribute { - Q_OBJECT - -public: - - Q_INVOKABLE PackedNormalAttribute(const QString& name = QString(), QRgb defaultValue = QRgb()); - - virtual bool merge(void*& parent, void* children[], bool postRead = false) const; - - virtual void* mix(void* first, void* second, float alpha) const; - - virtual void* blend(void* source, void* dest) const; + Q_INVOKABLE FloatAttribute(const QString& name = QString()); }; /// Packs a normal into an RGB value. @@ -422,42 +364,6 @@ QRgb packNormal(const glm::vec3& normal, int alpha); /// Unpacks a normal from an RGB value. glm::vec3 unpackNormal(QRgb value); -/// RGBA values for voxelized spanners. -class SpannerQRgbAttribute : public QRgbAttribute { - Q_OBJECT - -public: - - Q_INVOKABLE SpannerQRgbAttribute(const QString& name = QString(), QRgb defaultValue = QRgb()); - - virtual void read(Bitstream& in, void*& value, bool isLeaf) const; - virtual void write(Bitstream& out, void* value, bool isLeaf) const; - - virtual MetavoxelNode* createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const; - - virtual bool merge(void*& parent, void* children[], bool postRead = false) const; - - virtual AttributeValue inherit(const AttributeValue& parentValue) const; -}; - -/// Packed normals for voxelized spanners. -class SpannerPackedNormalAttribute : public PackedNormalAttribute { - Q_OBJECT - -public: - - Q_INVOKABLE SpannerPackedNormalAttribute(const QString& name = QString(), QRgb defaultValue = QRgb()); - - virtual void read(Bitstream& in, void*& value, bool isLeaf) const; - virtual void write(Bitstream& out, void* value, bool isLeaf) const; - - virtual MetavoxelNode* createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const; - - virtual bool merge(void*& parent, void* children[], bool postRead = false) const; - - virtual AttributeValue inherit(const AttributeValue& parentValue) const; -}; - typedef QExplicitlySharedDataPointer DataBlockPointer; /// Base class for blocks of data. @@ -494,124 +400,6 @@ protected: QMutex _encodedSubdivisionsMutex; }; -typedef QExplicitlySharedDataPointer HeightfieldHeightDataPointer; - -/// Contains a block of heightfield height data. -class HeightfieldHeightData : public DataBlock { -public: - - HeightfieldHeightData(const QByteArray& contents); - HeightfieldHeightData(Bitstream& in, int bytes); - HeightfieldHeightData(Bitstream& in, int bytes, const HeightfieldHeightDataPointer& reference); - HeightfieldHeightData(Bitstream& in, int bytes, const HeightfieldHeightDataPointer& ancestor, - const glm::vec3& minimum, float size); - - const QByteArray& getContents() const { return _contents; } - - void write(Bitstream& out); - void writeDelta(Bitstream& out, const HeightfieldHeightDataPointer& reference); - void writeSubdivided(Bitstream& out, const HeightfieldHeightDataPointer& ancestor, - const glm::vec3& minimum, float size); - -private: - - void read(Bitstream& in, int bytes); - void set(const QImage& image); - - QByteArray _contents; -}; - -/// An attribute that stores heightfield data. -class HeightfieldAttribute : public InlineAttribute { - Q_OBJECT - -public: - - Q_INVOKABLE HeightfieldAttribute(const QString& name = QString()); - - virtual void read(Bitstream& in, void*& value, bool isLeaf) const; - virtual void write(Bitstream& out, void* value, bool isLeaf) const; - - virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const; - virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const; - - virtual bool merge(void*& parent, void* children[], bool postRead = false) const; - - virtual AttributeValue inherit(const AttributeValue& parentValue) const; -}; - -typedef QExplicitlySharedDataPointer HeightfieldColorDataPointer; - -/// Contains a block of heightfield color data. -class HeightfieldColorData : public DataBlock { -public: - - HeightfieldColorData(const QByteArray& contents); - HeightfieldColorData(Bitstream& in, int bytes); - HeightfieldColorData(Bitstream& in, int bytes, const HeightfieldColorDataPointer& reference); - HeightfieldColorData(Bitstream& in, int bytes, const HeightfieldColorDataPointer& ancestor, - const glm::vec3& minimum, float size); - - const QByteArray& getContents() const { return _contents; } - - void write(Bitstream& out); - void writeDelta(Bitstream& out, const HeightfieldColorDataPointer& reference); - void writeSubdivided(Bitstream& out, const HeightfieldColorDataPointer& ancestor, - const glm::vec3& minimum, float size); - -private: - - void read(Bitstream& in, int bytes); - void set(const QImage& image); - - QByteArray _contents; -}; - -/// An attribute that stores heightfield colors. -class HeightfieldColorAttribute : public InlineAttribute { - Q_OBJECT - -public: - - Q_INVOKABLE HeightfieldColorAttribute(const QString& name = QString()); - - virtual void read(Bitstream& in, void*& value, bool isLeaf) const; - virtual void write(Bitstream& out, void* value, bool isLeaf) const; - - virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const; - virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const; - - virtual bool merge(void*& parent, void* children[], bool postRead = false) const; - - virtual AttributeValue inherit(const AttributeValue& parentValue) const; -}; - -typedef QExplicitlySharedDataPointer HeightfieldMaterialDataPointer; - -/// Contains a block of heightfield material data. -class HeightfieldMaterialData : public DataBlock { -public: - - HeightfieldMaterialData(const QByteArray& contents, - const QVector& materials = QVector()); - HeightfieldMaterialData(Bitstream& in, int bytes); - HeightfieldMaterialData(Bitstream& in, int bytes, const HeightfieldMaterialDataPointer& reference); - - const QByteArray& getContents() const { return _contents; } - - const QVector& getMaterials() const { return _materials; } - - void write(Bitstream& out); - void writeDelta(Bitstream& out, const HeightfieldMaterialDataPointer& reference); - -private: - - void read(Bitstream& in, int bytes); - - QByteArray _contents; - QVector _materials; -}; - /// Contains the description of a material. class MaterialObject : public SharedObject { Q_OBJECT @@ -635,25 +423,6 @@ private: float _scaleT; }; -/// An attribute that stores heightfield materials. -class HeightfieldMaterialAttribute : public InlineAttribute { - Q_OBJECT - -public: - - Q_INVOKABLE HeightfieldMaterialAttribute(const QString& name = QString()); - - virtual void read(Bitstream& in, void*& value, bool isLeaf) const; - virtual void write(Bitstream& out, void* value, bool isLeaf) const; - - virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const; - virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const; - - virtual bool merge(void*& parent, void* children[], bool postRead = false) const; - - virtual AttributeValue inherit(const AttributeValue& parentValue) const; -}; - /// Utility method for editing: given a material pointer and a list of materials, returns the corresponding material index, /// creating a new entry in the list if necessary. uchar getMaterialIndex(const SharedObjectPointer& material, QVector& materials, QByteArray& contents); diff --git a/libraries/metavoxels/src/Endpoint.h b/libraries/metavoxels/src/Endpoint.h index 816a1fe2a9..c64f29878d 100644 --- a/libraries/metavoxels/src/Endpoint.h +++ b/libraries/metavoxels/src/Endpoint.h @@ -16,6 +16,7 @@ #include "DatagramSequencer.h" #include "MetavoxelData.h" +#include "Spanner.h" class PacketRecord; diff --git a/libraries/metavoxels/src/MetavoxelClientManager.cpp b/libraries/metavoxels/src/MetavoxelClientManager.cpp index f1c086da67..660796994b 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.cpp +++ b/libraries/metavoxels/src/MetavoxelClientManager.cpp @@ -63,17 +63,85 @@ SharedObjectPointer MetavoxelClientManager::findFirstRaySpannerIntersection(cons return closestSpanner; } -void MetavoxelClientManager::setSphere(const glm::vec3& center, float radius, const QColor& color) { - Sphere* sphere = new Sphere(); - sphere->setTranslation(center); - sphere->setScale(radius); - sphere->setColor(color); - setSpanner(sphere); +class RayHeightfieldIntersectionVisitor : public RaySpannerIntersectionVisitor { +public: + + float intersectionDistance; + + RayHeightfieldIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const MetavoxelLOD& lod); + + virtual bool visitSpanner(Spanner* spanner, float distance); +}; + +RayHeightfieldIntersectionVisitor::RayHeightfieldIntersectionVisitor(const glm::vec3& origin, + const glm::vec3& direction, const MetavoxelLOD& lod) : + RaySpannerIntersectionVisitor(origin, direction, QVector() << + AttributeRegistry::getInstance()->getSpannersAttribute(), + QVector(), QVector(), lod), + intersectionDistance(FLT_MAX) { } -void MetavoxelClientManager::setSpanner(const SharedObjectPointer& object, bool reliable) { - MetavoxelEditMessage edit = { QVariant::fromValue(SetSpannerEdit(object)) }; - applyEdit(edit, reliable); +bool RayHeightfieldIntersectionVisitor::visitSpanner(Spanner* spanner, float distance) { + if (spanner->isHeightfield()) { + intersectionDistance = distance; + return false; + } + return true; +} + +bool MetavoxelClientManager::findFirstRayHeightfieldIntersection(const glm::vec3& origin, + const glm::vec3& direction, float& distance) { + RayHeightfieldIntersectionVisitor visitor(origin, direction, getLOD()); + guide(visitor); + if (visitor.intersectionDistance == FLT_MAX) { + return false; + } + distance = visitor.intersectionDistance; + return true; +} + +class HeightfieldHeightVisitor : public SpannerVisitor { +public: + + float height; + + HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location); + + virtual bool visit(Spanner* spanner); + virtual int visit(MetavoxelInfo& info); + +private: + + glm::vec3 _location; +}; + +HeightfieldHeightVisitor::HeightfieldHeightVisitor(const MetavoxelLOD& lod, const glm::vec3& location) : + SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute(), + QVector(), QVector(), lod), + height(-FLT_MAX), + _location(location) { +} + +bool HeightfieldHeightVisitor::visit(Spanner* spanner) { + height = qMax(height, spanner->getHeight(_location)); + return true; +} + +static const int REVERSE_ORDER = MetavoxelVisitor::encodeOrder(7, 6, 5, 4, 3, 2, 1, 0); + +int HeightfieldHeightVisitor::visit(MetavoxelInfo& info) { + if (_location.x < info.minimum.x || _location.z < info.minimum.z || _location.x > info.minimum.x + info.size || + _location.z > info.minimum.z + info.size) { + return STOP_RECURSION; + } + SpannerVisitor::visit(info); + return (height == -FLT_MAX) ? (info.isLeaf ? STOP_RECURSION : REVERSE_ORDER) : SHORT_CIRCUIT; +} + +float MetavoxelClientManager::getHeightfieldHeight(const glm::vec3& location) { + HeightfieldHeightVisitor visitor(getLOD(), location); + guide(visitor); + return visitor.height; } void MetavoxelClientManager::paintHeightfieldHeight(const glm::vec3& position, float radius, float height) { diff --git a/libraries/metavoxels/src/MetavoxelClientManager.h b/libraries/metavoxels/src/MetavoxelClientManager.h index adab59e0ff..8d029c172f 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.h +++ b/libraries/metavoxels/src/MetavoxelClientManager.h @@ -36,11 +36,11 @@ public: SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction, const AttributePointer& attribute, float& distance); + + bool findFirstRayHeightfieldIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance); - Q_INVOKABLE void setSphere(const glm::vec3& center, float radius, const QColor& color = QColor(Qt::gray)); + Q_INVOKABLE float getHeightfieldHeight(const glm::vec3& location); - Q_INVOKABLE void setSpanner(const SharedObjectPointer& object, bool reliable = false); - Q_INVOKABLE void paintHeightfieldHeight(const glm::vec3& position, float radius, float height); Q_INVOKABLE void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false); diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index b79903ba8a..c340a7dd4a 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -11,28 +11,15 @@ #include #include -#include -#include #include -#include - -#include - #include "MetavoxelData.h" -#include "MetavoxelUtil.h" -#include "ScriptCache.h" +#include "Spanner.h" REGISTER_META_OBJECT(MetavoxelGuide) REGISTER_META_OBJECT(DefaultMetavoxelGuide) -REGISTER_META_OBJECT(ScriptedMetavoxelGuide) -REGISTER_META_OBJECT(ThrobbingMetavoxelGuide) 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(); @@ -357,6 +344,10 @@ int SpannerReplaceVisitor::visit(MetavoxelInfo& info) { void MetavoxelData::replace(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& oldObject, const SharedObjectPointer& newObject) { Spanner* newSpanner = static_cast(newObject.data()); + if (!newSpanner) { + remove(attribute, bounds, granularity, oldObject); + return; + } if (bounds != newSpanner->getBounds() || granularity != newSpanner->getPlacementGranularity()) { // if the bounds have changed, we must remove and reinsert remove(attribute, bounds, granularity, oldObject); @@ -367,6 +358,45 @@ void MetavoxelData::replace(const AttributePointer& attribute, const Box& bounds guide(visitor); } +class SpannerFetchVisitor : public SpannerVisitor { +public: + + SpannerFetchVisitor(const AttributePointer& attribute, const Box& bounds, QVector& results); + + virtual bool visit(Spanner* spanner); + + virtual int visit(MetavoxelInfo& info); + +private: + + const Box& _bounds; + QVector& _results; +}; + +SpannerFetchVisitor::SpannerFetchVisitor(const AttributePointer& attribute, const Box& bounds, + QVector& results) : + SpannerVisitor(QVector() << attribute), + _bounds(bounds), + _results(results) { +} + +bool SpannerFetchVisitor::visit(Spanner* spanner) { + if (spanner->getBounds().intersects(_bounds)) { + _results.append(spanner); + } + return true; +} + +int SpannerFetchVisitor::visit(MetavoxelInfo& info) { + return info.getBounds().intersects(_bounds) ? SpannerVisitor::visit(info) : STOP_RECURSION; +} + +void MetavoxelData::getIntersecting(const AttributePointer& attribute, const Box& bounds, + QVector& results) { + SpannerFetchVisitor visitor(attribute, bounds, results); + guide(visitor); +} + void MetavoxelData::clear(const AttributePointer& attribute) { MetavoxelNode* node = _roots.take(attribute); if (node) { @@ -401,7 +431,7 @@ private: FirstRaySpannerIntersectionVisitor::FirstRaySpannerIntersectionVisitor( const glm::vec3& origin, const glm::vec3& direction, const AttributePointer& attribute, const MetavoxelLOD& lod) : RaySpannerIntersectionVisitor(origin, direction, QVector() << attribute, - QVector(), QVector(), QVector(), lod), + QVector(), QVector(), lod), _spanner(NULL) { } @@ -1367,12 +1397,10 @@ MetavoxelVisitation& MetavoxelVisitor::acquireVisitation() { return _visitations[_depth]; } -SpannerVisitor::SpannerVisitor(const QVector& spannerInputs, const QVector& spannerMasks, - const QVector& inputs, const QVector& outputs, - const MetavoxelLOD& lod, int order) : - MetavoxelVisitor(inputs + spannerInputs + spannerMasks, outputs, lod), +SpannerVisitor::SpannerVisitor(const QVector& spannerInputs, const QVector& inputs, + const QVector& outputs, const MetavoxelLOD& lod, int order) : + MetavoxelVisitor(inputs + spannerInputs, outputs, lod), _spannerInputCount(spannerInputs.size()), - _spannerMaskCount(spannerMasks.size()), _order(order) { } @@ -1382,34 +1410,15 @@ void SpannerVisitor::prepare(MetavoxelData* data) { } int SpannerVisitor::visit(MetavoxelInfo& info) { - for (int end = _inputs.size() - _spannerMaskCount, i = end - _spannerInputCount, j = end; i < end; i++, j++) { + for (int end = _inputs.size(), i = end - _spannerInputCount; i < end; i++) { foreach (const SharedObjectPointer& object, info.inputValues.at(i).getInlineValue()) { Spanner* spanner = static_cast(object.data()); - if (!(spanner->isMasked() && j < _inputs.size()) && spanner->testAndSetVisited(_visit) && - !visit(spanner, glm::vec3(), 0.0f)) { + if (spanner->testAndSetVisited(_visit) && !visit(spanner)) { return SHORT_CIRCUIT; } } } - if (!info.isLeaf) { - return _order; - } - for (int i = _inputs.size() - _spannerMaskCount; i < _inputs.size(); i++) { - float maskValue = info.inputValues.at(i).getInlineValue(); - if (maskValue < 0.5f) { - const MetavoxelInfo* nextInfo = &info; - do { - foreach (const SharedObjectPointer& object, nextInfo->inputValues.at( - i - _spannerInputCount).getInlineValue()) { - Spanner* spanner = static_cast(object.data()); - if (spanner->isMasked() && !visit(spanner, info.minimum, info.size)) { - return SHORT_CIRCUIT; - } - } - } while ((nextInfo = nextInfo->parentInfo)); - } - } - return STOP_RECURSION; + return info.isLeaf ? STOP_RECURSION : _order; } RayIntersectionVisitor::RayIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, @@ -1429,11 +1438,10 @@ int RayIntersectionVisitor::visit(MetavoxelInfo& info) { } RaySpannerIntersectionVisitor::RaySpannerIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, - const QVector& spannerInputs, const QVector& spannerMasks, - const QVector& inputs, const QVector& outputs, const MetavoxelLOD& lod) : - RayIntersectionVisitor(origin, direction, inputs + spannerInputs + spannerMasks, outputs, lod), - _spannerInputCount(spannerInputs.size()), - _spannerMaskCount(spannerMasks.size()) { + const QVector& spannerInputs, const QVector& inputs, + const QVector& outputs, const MetavoxelLOD& lod) : + RayIntersectionVisitor(origin, direction, inputs + spannerInputs, outputs, lod), + _spannerInputCount(spannerInputs.size()) { } void RaySpannerIntersectionVisitor::prepare(MetavoxelData* data) { @@ -1453,12 +1461,12 @@ bool operator<(const SpannerDistance& first, const SpannerDistance& second) { int RaySpannerIntersectionVisitor::visit(MetavoxelInfo& info, float distance) { QVarLengthArray spannerDistances; - for (int end = _inputs.size() - _spannerMaskCount, i = end - _spannerInputCount, j = end; i < end; i++, j++) { + for (int end = _inputs.size(), i = end - _spannerInputCount; i < end; i++) { foreach (const SharedObjectPointer& object, info.inputValues.at(i).getInlineValue()) { Spanner* spanner = static_cast(object.data()); - if (!(spanner->isMasked() && j < _inputs.size()) && spanner->testAndSetVisited(_visit)) { + if (spanner->testAndSetVisited(_visit)) { SpannerDistance spannerDistance = { spanner }; - if (spanner->findRayIntersection(_origin, _direction, glm::vec3(), 0.0f, spannerDistance.distance)) { + if (spanner->findRayIntersection(_origin, _direction, spannerDistance.distance)) { spannerDistances.append(spannerDistance); } } @@ -1470,36 +1478,7 @@ int RaySpannerIntersectionVisitor::visit(MetavoxelInfo& info, float distance) { } } } - if (!info.isLeaf) { - return _order; - } - for (int i = _inputs.size() - _spannerMaskCount; i < _inputs.size(); i++) { - float maskValue = info.inputValues.at(i).getInlineValue(); - if (maskValue < 0.5f) { - const MetavoxelInfo* nextInfo = &info; - do { - foreach (const SharedObjectPointer& object, nextInfo->inputValues.at( - i - _spannerInputCount).getInlineValue()) { - Spanner* spanner = static_cast(object.data()); - if (spanner->isMasked()) { - SpannerDistance spannerDistance = { spanner }; - if (spanner->findRayIntersection(_origin, _direction, - info.minimum, info.size, spannerDistance.distance)) { - spannerDistances.append(spannerDistance); - } - } - } - } while ((nextInfo = nextInfo->parentInfo)); - - qStableSort(spannerDistances); - foreach (const SpannerDistance& spannerDistance, spannerDistances) { - if (!visitSpanner(spannerDistance.spanner, spannerDistance.distance)) { - return SHORT_CIRCUIT; - } - } - } - } - return STOP_RECURSION; + return info.isLeaf ? STOP_RECURSION : _order; } bool MetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) { @@ -1765,143 +1744,6 @@ bool DefaultMetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) { return true; } -ThrobbingMetavoxelGuide::ThrobbingMetavoxelGuide() : _rate(10.0) { -} - -bool ThrobbingMetavoxelGuide::guide(MetavoxelVisitation& visitation) { - AttributePointer colorAttribute = AttributeRegistry::getInstance()->getColorAttribute(); - for (int i = 0; i < visitation.info.inputValues.size(); i++) { - AttributeValue& attributeValue = visitation.info.inputValues[i]; - if (attributeValue.getAttribute() == colorAttribute) { - QRgb base = attributeValue.getInlineValue(); - double seconds = QDateTime::currentMSecsSinceEpoch() / 1000.0; - double amplitude = sin(_rate * seconds) * 0.5 + 0.5; - attributeValue.setInlineValue(qRgba(qRed(base) * amplitude, qGreen(base) * amplitude, - qBlue(base) * amplitude, qAlpha(base))); - } - } - - return DefaultMetavoxelGuide::guide(visitation); -} - -static QScriptValue getAttributes(QScriptEngine* engine, ScriptedMetavoxelGuide* guide, - const QVector& attributes) { - - QScriptValue attributesValue = engine->newArray(attributes.size()); - for (int i = 0; i < attributes.size(); i++) { - attributesValue.setProperty(i, engine->newQObject(attributes.at(i).data(), QScriptEngine::QtOwnership, - QScriptEngine::PreferExistingWrapperObject)); - } - return attributesValue; -} - -QScriptValue ScriptedMetavoxelGuide::getInputs(QScriptContext* context, QScriptEngine* engine) { - ScriptedMetavoxelGuide* guide = static_cast(context->callee().data().toVariant().value()); - return getAttributes(engine, guide, guide->_visitation->visitor->getInputs()); -} - -QScriptValue ScriptedMetavoxelGuide::getOutputs(QScriptContext* context, QScriptEngine* engine) { - ScriptedMetavoxelGuide* guide = static_cast(context->callee().data().toVariant().value()); - return getAttributes(engine, guide, guide->_visitation->visitor->getOutputs()); -} - -QScriptValue ScriptedMetavoxelGuide::visit(QScriptContext* context, QScriptEngine* engine) { - ScriptedMetavoxelGuide* guide = static_cast(context->callee().data().toVariant().value()); - - // start with the basics, including inherited attribute values - QScriptValue infoValue = context->argument(0); - QScriptValue minimum = infoValue.property(guide->_minimumHandle); - MetavoxelInfo info(NULL, 0, 0); - info.inputValues = guide->_visitation->info.inputValues; - info.outputValues = guide->_visitation->info.outputValues; - info.minimum = glm::vec3(minimum.property(0).toNumber(), minimum.property(1).toNumber(), minimum.property(2).toNumber()); - info.size = (float)infoValue.property(guide->_sizeHandle).toNumber(); - info.isLeaf = infoValue.property(guide->_isLeafHandle).toBool(); - - // extract and convert the values provided by the script - QScriptValue inputValues = infoValue.property(guide->_inputValuesHandle); - const QVector& inputs = guide->_visitation->visitor->getInputs(); - for (int i = 0; i < inputs.size(); i++) { - QScriptValue attributeValue = inputValues.property(i); - if (attributeValue.isValid()) { - info.inputValues[i] = AttributeValue(inputs.at(i), - inputs.at(i)->createFromScript(attributeValue, engine)); - } - } - - QScriptValue result = guide->_visitation->visitor->visit(info); - - // destroy any created values - for (int i = 0; i < inputs.size(); i++) { - if (inputValues.property(i).isValid()) { - info.inputValues[i].getAttribute()->destroy(info.inputValues[i].getValue()); - } - } - - return result; -} - -ScriptedMetavoxelGuide::ScriptedMetavoxelGuide() { -} - -bool ScriptedMetavoxelGuide::guide(MetavoxelVisitation& visitation) { - QScriptValue guideFunction; - if (_guideFunction) { - guideFunction = _guideFunction->getValue(); - - } else if (_url.isValid()) { - _guideFunction = ScriptCache::getInstance()->getValue(_url); - guideFunction = _guideFunction->getValue(); - } - if (!guideFunction.isValid()) { - // before we load, just use the default behavior - return DefaultMetavoxelGuide::guide(visitation); - } - QScriptEngine* engine = guideFunction.engine(); - if (!_minimumHandle.isValid()) { - _minimumHandle = engine->toStringHandle("minimum"); - _sizeHandle = engine->toStringHandle("size"); - _inputValuesHandle = engine->toStringHandle("inputValues"); - _outputValuesHandle = engine->toStringHandle("outputValues"); - _isLeafHandle = engine->toStringHandle("isLeaf"); - _getInputsFunction = engine->newFunction(getInputs, 0); - _getOutputsFunction = engine->newFunction(getOutputs, 0); - _visitFunction = engine->newFunction(visit, 1); - _info = engine->newObject(); - _minimum = engine->newArray(3); - - _arguments.clear(); - _arguments.append(engine->newObject()); - QScriptValue visitor = engine->newObject(); - visitor.setProperty("getInputs", _getInputsFunction); - visitor.setProperty("getOutputs", _getOutputsFunction); - visitor.setProperty("visit", _visitFunction); - _arguments[0].setProperty("visitor", visitor); - _arguments[0].setProperty("info", _info); - _info.setProperty(_minimumHandle, _minimum); - } - QScriptValue data = engine->newVariant(QVariant::fromValue(this)); - _getInputsFunction.setData(data); - _visitFunction.setData(data); - _minimum.setProperty(0, visitation.info.minimum.x); - _minimum.setProperty(1, visitation.info.minimum.y); - _minimum.setProperty(2, visitation.info.minimum.z); - _info.setProperty(_sizeHandle, visitation.info.size); - _info.setProperty(_isLeafHandle, visitation.info.isLeaf); - _visitation = &visitation; - guideFunction.call(QScriptValue(), _arguments); - if (engine->hasUncaughtException()) { - qDebug() << "Script error: " << engine->uncaughtException().toString(); - } - return true; -} - -void ScriptedMetavoxelGuide::setURL(const ParameterizedURL& url) { - _url = url; - _guideFunction.reset(); - _minimumHandle = QScriptString(); -} - MetavoxelVisitation::MetavoxelVisitation(MetavoxelVisitation* previous, MetavoxelVisitor* visitor, int inputNodesSize, int outputNodesSize) : previous(previous), @@ -1991,672 +1833,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), - _masked(false) { -} - -void Spanner::setBounds(const Box& bounds) { - if (_bounds == bounds) { - return; - } - emit boundsWillChange(); - emit boundsChanged(_bounds = bounds); -} - -const QVector& Spanner::getAttributes() const { - static QVector emptyVector; - return emptyVector; -} - -const QVector& Spanner::getVoxelizedAttributes() const { - static QVector emptyVector; - return emptyVector; -} - -bool Spanner::getAttributeValues(MetavoxelInfo& info, bool force) const { - return false; -} - -bool Spanner::blendAttributeValues(MetavoxelInfo& info, bool force) const { - return false; -} - -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(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, - const glm::vec3& clipMinimum, float clipSize, 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& Spanner::getMaterials() { - static QVector 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, const glm::vec3& clipMinimum, float clipSize) { - // nothing by default -} - -bool SpannerRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - const glm::vec3& clipMinimum, float clipSize, 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(); -} - -const QVector& Sphere::getAttributes() const { - static QVector attributes = QVector() << - AttributeRegistry::getInstance()->getColorAttribute() << AttributeRegistry::getInstance()->getNormalAttribute(); - return attributes; -} - -const QVector& Sphere::getVoxelizedAttributes() const { - static QVector attributes = QVector() << - AttributeRegistry::getInstance()->getSpannerColorAttribute() << - AttributeRegistry::getInstance()->getSpannerNormalAttribute(); - return attributes; -} - -bool Sphere::getAttributeValues(MetavoxelInfo& info, bool force) const { - // bounds check - Box bounds = info.getBounds(); - if (!(force || getBounds().intersects(bounds))) { - return false; - } - // count the points inside the sphere - int pointsWithin = 0; - for (int i = 0; i < Box::VERTEX_COUNT; i++) { - if (glm::distance(bounds.getVertex(i), getTranslation()) <= getScale()) { - pointsWithin++; - } - } - if (pointsWithin == Box::VERTEX_COUNT) { - // entirely contained - info.outputValues[0] = AttributeValue(getAttributes().at(0), encodeInline(_color.rgba())); - info.outputValues[1] = getNormal(info, _color.alpha()); - return false; - } - if (force || info.size <= getVoxelizationGranularity()) { - // best guess - if (pointsWithin > 0) { - int alpha = _color.alpha() * pointsWithin / Box::VERTEX_COUNT; - info.outputValues[0] = AttributeValue(getAttributes().at(0), encodeInline(qRgba( - _color.red(), _color.green(), _color.blue(), alpha))); - info.outputValues[1] = getNormal(info, alpha); - } - return false; - } - return true; -} - -bool Sphere::blendAttributeValues(MetavoxelInfo& info, bool force) const { - // bounds check - Box bounds = info.getBounds(); - if (!(force || getBounds().intersects(bounds))) { - return false; - } - // count the points inside the sphere - int pointsWithin = 0; - for (int i = 0; i < Box::VERTEX_COUNT; i++) { - if (glm::distance(bounds.getVertex(i), getTranslation()) <= getScale()) { - pointsWithin++; - } - } - if (pointsWithin == Box::VERTEX_COUNT) { - // entirely contained - info.outputValues[0] = AttributeValue(getAttributes().at(0), encodeInline(_color.rgba())); - info.outputValues[1] = getNormal(info, _color.alpha()); - return false; - } - if (force || info.size <= getVoxelizationGranularity()) { - // best guess - if (pointsWithin > 0) { - const AttributeValue& oldColor = info.outputValues.at(0).getAttribute() ? - info.outputValues.at(0) : info.inputValues.at(0); - const AttributeValue& oldNormal = info.outputValues.at(1).getAttribute() ? - info.outputValues.at(1) : info.inputValues.at(1); - int oldAlpha = qAlpha(oldColor.getInlineValue()); - int newAlpha = _color.alpha() * pointsWithin / Box::VERTEX_COUNT; - float combinedAlpha = (float)newAlpha / (oldAlpha + newAlpha); - int baseAlpha = _color.alpha() * pointsWithin / Box::VERTEX_COUNT; - info.outputValues[0].mix(oldColor, AttributeValue(getAttributes().at(0), - encodeInline(qRgba(_color.red(), _color.green(), _color.blue(), baseAlpha))), combinedAlpha); - info.outputValues[1].mix(oldNormal, getNormal(info, baseAlpha), combinedAlpha); - } - return false; - } - return true; -} - -bool Sphere::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - const glm::vec3& clipMinimum, float clipSize, 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)); -} - -AttributeValue Sphere::getNormal(MetavoxelInfo& info, int alpha) const { - glm::vec3 normal = info.getCenter() - getTranslation(); - float length = glm::length(normal); - QRgb color; - if (alpha != 0 && length > EPSILON) { - const float NORMAL_SCALE = 127.0f; - float scale = NORMAL_SCALE / length; - const int BYTE_MASK = 0xFF; - color = qRgba((int)(normal.x * scale) & BYTE_MASK, (int)(normal.y * scale) & BYTE_MASK, - (int)(normal.z * scale) & BYTE_MASK, alpha); - - } else { - color = QRgb(); - } - return AttributeValue(getAttributes().at(1), encodeInline(color)); -} - -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, - const glm::vec3& clipMinimum, float clipSize, float& distance) const { - // delegate to renderer, if we have one - return _renderer ? _renderer->findRayIntersection(origin, direction, clipMinimum, clipSize, distance) : - Spanner::findRayIntersection(origin, direction, clipMinimum, clipSize, 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& 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& 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; -} diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 3efb0fc8f2..7bfd2a7522 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -12,13 +12,10 @@ #ifndef hifi_MetavoxelData_h #define hifi_MetavoxelData_h -#include #include #include #include #include -#include -#include #include #include @@ -26,16 +23,12 @@ #include "AttributeRegistry.h" #include "MetavoxelUtil.h" -class QScriptContext; - class MetavoxelInfo; 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. @@ -109,6 +102,9 @@ public: void replace(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& oldObject, const SharedObjectPointer& newObject); + /// Retrieves all spanners that intersect the specified bounds. + void getIntersecting(const AttributePointer& attribute, const Box& bounds, QVector& results); + /// Clears all data in the specified attribute layer. void clear(const AttributePointer& attribute); @@ -373,16 +369,14 @@ class SpannerVisitor : public MetavoxelVisitor { public: SpannerVisitor(const QVector& spannerInputs, - const QVector& spannerMasks = QVector(), const QVector& inputs = QVector(), const QVector& outputs = QVector(), const MetavoxelLOD& lod = MetavoxelLOD(), int order = DEFAULT_ORDER); - /// Visits a spanner (or part thereof). - /// \param clipSize the size of the clip volume, or zero if unclipped + /// Visits a spanner. /// \return true to continue, false to short-circuit the tour - virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) = 0; + virtual bool visit(Spanner* spanner) = 0; virtual void prepare(MetavoxelData* data); virtual int visit(MetavoxelInfo& info); @@ -390,7 +384,6 @@ public: protected: int _spannerInputCount; - int _spannerMaskCount; int _order; int _visit; }; @@ -422,12 +415,11 @@ public: RaySpannerIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const QVector& spannerInputs, - const QVector& spannerMasks = QVector(), const QVector& inputs = QVector(), const QVector& outputs = QVector(), const MetavoxelLOD& lod = MetavoxelLOD()); - /// Visits a spannerthat the ray intersects. + /// Visits a spanner that the ray intersects. /// \return true to continue, false to short-circuit the tour virtual bool visitSpanner(Spanner* spanner, float distance) = 0; @@ -437,7 +429,6 @@ public: protected: int _spannerInputCount; - int _spannerMaskCount; int _visit; }; @@ -468,62 +459,6 @@ public: virtual bool guideToDifferent(MetavoxelVisitation& visitation); }; -/// A temporary test guide that just makes the existing voxels throb with delight. -class ThrobbingMetavoxelGuide : public DefaultMetavoxelGuide { - Q_OBJECT - Q_PROPERTY(float rate MEMBER _rate) - -public: - - Q_INVOKABLE ThrobbingMetavoxelGuide(); - - virtual bool guide(MetavoxelVisitation& visitation); - -private: - - float _rate; -}; - -/// Represents a guide implemented in Javascript. -class ScriptedMetavoxelGuide : public DefaultMetavoxelGuide { - Q_OBJECT - Q_PROPERTY(ParameterizedURL url MEMBER _url WRITE setURL) - -public: - - Q_INVOKABLE ScriptedMetavoxelGuide(); - - virtual bool guide(MetavoxelVisitation& visitation); - -public slots: - - void setURL(const ParameterizedURL& url); - -private: - - static QScriptValue getInputs(QScriptContext* context, QScriptEngine* engine); - static QScriptValue getOutputs(QScriptContext* context, QScriptEngine* engine); - static QScriptValue visit(QScriptContext* context, QScriptEngine* engine); - - ParameterizedURL _url; - - QSharedPointer _guideFunction; - - QScriptString _minimumHandle; - QScriptString _sizeHandle; - QScriptString _inputValuesHandle; - QScriptString _outputValuesHandle; - QScriptString _isLeafHandle; - QScriptValueList _arguments; - QScriptValue _getInputsFunction; - QScriptValue _getOutputsFunction; - QScriptValue _visitFunction; - QScriptValue _info; - QScriptValue _minimum; - - MetavoxelVisitation* _visitation; -}; - /// Contains the state associated with a visit to a metavoxel system. class MetavoxelVisitation { public: @@ -592,310 +527,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) - Q_PROPERTY(float masked MEMBER _masked 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 setMasked(bool masked) { _masked = masked; } - bool isMasked() const { return _masked; } - - /// Returns a reference to the list of attributes associated with this spanner. - virtual const QVector& getAttributes() const; - - /// Returns a reference to the list of corresponding attributes that we voxelize the spanner into. - virtual const QVector& getVoxelizedAttributes() const; - - /// Sets the attribute values associated with this spanner in the supplied info. - /// \return true to recurse, false to stop - virtual bool getAttributeValues(MetavoxelInfo& info, bool force = false) const; - - /// Blends the attribute values associated with this spanner into the supplied info. - /// \param force if true, blend even if we would normally subdivide - /// \return true to recurse, false to stop - virtual bool blendAttributeValues(MetavoxelInfo& info, bool force = false) const; - - /// 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. - /// \param clipSize the size of the clip region, or zero if unclipped - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - const glm::vec3& clipMinimum, float clipSize, 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& 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 _masked; - QHash _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, const glm::vec3& clipMinimum, float clipSize); - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - const glm::vec3& clipMinimum, float clipSize, 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 const QVector& getAttributes() const; - virtual const QVector& getVoxelizedAttributes() const; - virtual bool getAttributeValues(MetavoxelInfo& info, bool force = false) const; - virtual bool blendAttributeValues(MetavoxelInfo& info, bool force = false) const; - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - const glm::vec3& clipMinimum, float clipSize, 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(); - -private: - - AttributeValue getNormal(MetavoxelInfo& info, int alpha) const; -}; - -/// 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, - const glm::vec3& clipMinimum, float clipSize, 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& 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& 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 _materials; -}; - #endif // hifi_MetavoxelData_h diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index c9c2d32b7e..a4d2569de0 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -10,6 +10,7 @@ // #include "MetavoxelMessages.h" +#include "Spanner.h" void MetavoxelEditMessage::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { static_cast(edit.data())->apply(data, objects); @@ -39,8 +40,7 @@ private: }; BoxSetEditVisitor::BoxSetEditVisitor(const BoxSetEdit& edit) : - MetavoxelVisitor(QVector(), QVector() << edit.value.getAttribute() << - AttributeRegistry::getInstance()->getSpannerMaskAttribute()), + MetavoxelVisitor(QVector(), QVector() << edit.value.getAttribute()), _edit(edit) { } @@ -55,57 +55,17 @@ int BoxSetEditVisitor::visit(MetavoxelInfo& info) { float volume = (size.x * size.y * size.z) / (info.size * info.size * info.size); if (volume >= 1.0f) { info.outputValues[0] = _edit.value; - info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline(1.0f)); return STOP_RECURSION; // entirely contained } if (info.size <= _edit.granularity) { if (volume >= 0.5f) { info.outputValues[0] = _edit.value; - info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline(1.0f)); } return STOP_RECURSION; // reached granularity limit; take best guess } return DEFAULT_ORDER; // subdivide } -class GatherUnmaskedSpannersVisitor : public SpannerVisitor { -public: - - GatherUnmaskedSpannersVisitor(const Box& bounds); - - const QList& getUnmaskedSpanners() const { return _unmaskedSpanners; } - - virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize); - -private: - - Box _bounds; - QList _unmaskedSpanners; -}; - -GatherUnmaskedSpannersVisitor::GatherUnmaskedSpannersVisitor(const Box& bounds) : - SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute()), - _bounds(bounds) { -} - -bool GatherUnmaskedSpannersVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) { - if (!spanner->isMasked() && spanner->getBounds().intersects(_bounds)) { - _unmaskedSpanners.append(spanner); - } - return true; -} - -static void setIntersectingMasked(const Box& bounds, MetavoxelData& data) { - GatherUnmaskedSpannersVisitor visitor(bounds); - data.guide(visitor); - - foreach (const SharedObjectPointer& object, visitor.getUnmaskedSpanners()) { - Spanner* newSpanner = static_cast(object->clone(true)); - newSpanner->setMasked(true); - data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), object, newSpanner); - } -} - void BoxSetEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { // expand to fit the entire edit while (!data.getBounds().contains(region)) { @@ -114,9 +74,6 @@ void BoxSetEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) BoxSetEditVisitor setVisitor(*this); data.guide(setVisitor); - - // flip the mask flag of all intersecting spanners - setIntersectingMasked(region, data); } GlobalSetEdit::GlobalSetEdit(const OwnedAttributeValue& value) : @@ -155,56 +112,8 @@ InsertSpannerEdit::InsertSpannerEdit(const AttributePointer& attribute, const Sh spanner(spanner) { } -class UpdateSpannerVisitor : public MetavoxelVisitor { -public: - - UpdateSpannerVisitor(const QVector& attributes, Spanner* spanner); - - virtual int visit(MetavoxelInfo& info); - -private: - - Spanner* _spanner; - float _voxelizationSize; - int _steps; -}; - -UpdateSpannerVisitor::UpdateSpannerVisitor(const QVector& attributes, Spanner* spanner) : - MetavoxelVisitor(QVector() << attributes << AttributeRegistry::getInstance()->getSpannersAttribute(), - attributes), - _spanner(spanner), - _voxelizationSize(qMax(spanner->getBounds().getLongestSide(), spanner->getPlacementGranularity()) * 2.0f / - AttributeRegistry::getInstance()->getSpannersAttribute()->getLODThresholdMultiplier()), - _steps(glm::round(logf(AttributeRegistry::getInstance()->getSpannersAttribute()->getLODThresholdMultiplier()) / - logf(2.0f) - 2.0f)) { -} - -int UpdateSpannerVisitor::visit(MetavoxelInfo& info) { - if (!info.getBounds().intersects(_spanner->getBounds())) { - return STOP_RECURSION; - } - MetavoxelInfo* parentInfo = info.parentInfo; - for (int i = 0; i < _steps && parentInfo; i++) { - parentInfo = parentInfo->parentInfo; - } - for (int i = 0; i < _outputs.size(); i++) { - info.outputValues[i] = AttributeValue(_outputs.at(i)); - } - if (parentInfo) { - foreach (const SharedObjectPointer& object, - parentInfo->inputValues.at(_outputs.size()).getInlineValue()) { - static_cast(object.data())->blendAttributeValues(info, true); - } - } - return (info.size > _voxelizationSize) ? DEFAULT_ORDER : STOP_RECURSION; -} - void InsertSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - data.insert(attribute, this->spanner); - - Spanner* spanner = static_cast(this->spanner.data()); - UpdateSpannerVisitor visitor(spanner->getVoxelizedAttributes(), spanner); - data.guide(visitor); + data.insert(attribute, spanner); } RemoveSpannerEdit::RemoveSpannerEdit(const AttributePointer& attribute, int id) : @@ -218,99 +127,15 @@ void RemoveSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& o qDebug() << "Missing object to remove" << id; return; } - // keep a strong reference to the object - SharedObjectPointer sharedPointer = object; data.remove(attribute, object); - - Spanner* spanner = static_cast(object); - UpdateSpannerVisitor visitor(spanner->getVoxelizedAttributes(), spanner); - data.guide(visitor); } ClearSpannersEdit::ClearSpannersEdit(const AttributePointer& attribute) : attribute(attribute) { } -class GatherSpannerAttributesVisitor : public SpannerVisitor { -public: - - GatherSpannerAttributesVisitor(const AttributePointer& attribute); - - const QSet& getAttributes() const { return _attributes; } - - virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize); - -protected: - - QSet _attributes; -}; - -GatherSpannerAttributesVisitor::GatherSpannerAttributesVisitor(const AttributePointer& attribute) : - SpannerVisitor(QVector() << attribute) { -} - -bool GatherSpannerAttributesVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) { - foreach (const AttributePointer& attribute, spanner->getVoxelizedAttributes()) { - _attributes.insert(attribute); - } - return true; -} - void ClearSpannersEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - // find all the spanner attributes - GatherSpannerAttributesVisitor visitor(attribute); - data.guide(visitor); - data.clear(attribute); - foreach (const AttributePointer& attribute, visitor.getAttributes()) { - data.clear(attribute); - } -} - -SetSpannerEdit::SetSpannerEdit(const SharedObjectPointer& spanner) : - spanner(spanner) { -} - -class SetSpannerEditVisitor : public MetavoxelVisitor { -public: - - SetSpannerEditVisitor(const QVector& attributes, Spanner* spanner); - - virtual int visit(MetavoxelInfo& info); - -private: - - Spanner* _spanner; -}; - -SetSpannerEditVisitor::SetSpannerEditVisitor(const QVector& attributes, Spanner* spanner) : - MetavoxelVisitor(attributes, QVector() << attributes << - AttributeRegistry::getInstance()->getSpannerMaskAttribute()), - _spanner(spanner) { -} - -int SetSpannerEditVisitor::visit(MetavoxelInfo& info) { - if (_spanner->blendAttributeValues(info)) { - return DEFAULT_ORDER; - } - if (info.outputValues.at(0).getAttribute()) { - info.outputValues.last() = AttributeValue(_outputs.last(), encodeInline(1.0f)); - } - return STOP_RECURSION; -} - -void SetSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - Spanner* spanner = static_cast(this->spanner.data()); - - // expand to fit the entire spanner - while (!data.getBounds().contains(spanner->getBounds())) { - data.expand(); - } - - SetSpannerEditVisitor visitor(spanner->getAttributes(), spanner); - data.guide(visitor); - - setIntersectingMasked(spanner->getBounds(), data); } SetDataEdit::SetDataEdit(const glm::vec3& minimum, const MetavoxelData& data, bool blend) : @@ -329,87 +154,20 @@ PaintHeightfieldHeightEdit::PaintHeightfieldHeightEdit(const glm::vec3& position height(height) { } -class PaintHeightfieldHeightEditVisitor : public MetavoxelVisitor { -public: - - PaintHeightfieldHeightEditVisitor(const PaintHeightfieldHeightEdit& edit); - - virtual int visit(MetavoxelInfo& info); - -private: - - PaintHeightfieldHeightEdit _edit; - Box _bounds; -}; - -PaintHeightfieldHeightEditVisitor::PaintHeightfieldHeightEditVisitor(const PaintHeightfieldHeightEdit& edit) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute(), - QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute()), - _edit(edit) { - - glm::vec3 extents(_edit.radius, _edit.radius, _edit.radius); - _bounds = Box(_edit.position - extents, _edit.position + extents); -} - -const int EIGHT_BIT_MAXIMUM = 255; - -int PaintHeightfieldHeightEditVisitor::visit(MetavoxelInfo& info) { - if (!info.getBounds().intersects(_bounds)) { - return STOP_RECURSION; - } - if (!info.isLeaf) { - return DEFAULT_ORDER; - } - HeightfieldHeightDataPointer pointer = info.inputValues.at(0).getInlineValue(); - if (!pointer) { - return STOP_RECURSION; - } - QByteArray contents(pointer->getContents()); - int size = glm::sqrt((float)contents.size()); - int highest = size - 1; - float heightScale = size / info.size; - - glm::vec3 center = (_edit.position - info.minimum) * heightScale; - float scaledRadius = _edit.radius * heightScale; - glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius); - - glm::vec3 start = glm::floor(center - extents); - glm::vec3 end = glm::ceil(center + extents); - - // raise/lower all points within the radius - float z = qMax(start.z, 0.0f); - float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest); - uchar* lineDest = (uchar*)contents.data() + (int)z * size + (int)startX; - float squaredRadius = scaledRadius * scaledRadius; - float squaredRadiusReciprocal = 1.0f / squaredRadius; - float scaledHeight = _edit.height * EIGHT_BIT_MAXIMUM / info.size; - bool changed = false; - for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) { - uchar* dest = lineDest; - for (float x = startX; x <= endX; x += 1.0f, dest++) { - float dx = x - center.x, dz = z - center.z; - float distanceSquared = dx * dx + dz * dz; - if (distanceSquared <= squaredRadius) { - // height falls off towards edges - int value = *dest + scaledHeight * (squaredRadius - distanceSquared) * squaredRadiusReciprocal; - if (value != *dest) { - *dest = qMin(qMax(value, 0), EIGHT_BIT_MAXIMUM); - changed = true; - } - } - } - lineDest += size; - } - if (changed) { - HeightfieldHeightDataPointer newPointer(new HeightfieldHeightData(contents)); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(newPointer)); - } - return STOP_RECURSION; -} - void PaintHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - PaintHeightfieldHeightEditVisitor visitor(*this); - data.guide(visitor); + // increase the extents slightly to include neighboring tiles + const float RADIUS_EXTENSION = 1.1f; + glm::vec3 extents = glm::vec3(radius, radius, radius) * RADIUS_EXTENSION; + QVector results; + data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(), + Box(position - extents, position + extents), results); + + foreach (const SharedObjectPointer& spanner, results) { + Spanner* newSpanner = static_cast(spanner.data())->paintHeight(position, radius, height); + if (newSpanner != spanner) { + data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), spanner, newSpanner); + } + } } MaterialEdit::MaterialEdit(const SharedObjectPointer& material, const QColor& averageColor) : @@ -417,130 +175,6 @@ MaterialEdit::MaterialEdit(const SharedObjectPointer& material, const QColor& av averageColor(averageColor) { } -class PaintHeightfieldMaterialEditVisitor : public MetavoxelVisitor { -public: - - PaintHeightfieldMaterialEditVisitor(const glm::vec3& position, float radius, - const SharedObjectPointer& material, const QColor& color); - - virtual int visit(MetavoxelInfo& info); - -private: - - glm::vec3 _position; - float _radius; - SharedObjectPointer _material; - QColor _color; - Box _bounds; -}; - -PaintHeightfieldMaterialEditVisitor::PaintHeightfieldMaterialEditVisitor(const glm::vec3& position, float radius, - const SharedObjectPointer& material, const QColor& color) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute() << - AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), QVector() << - AttributeRegistry::getInstance()->getHeightfieldColorAttribute() << - AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute()), - _position(position), - _radius(radius), - _material(material), - _color(color) { - - const float LARGE_EXTENT = 100000.0f; - glm::vec3 extents(_radius, LARGE_EXTENT, _radius); - _bounds = Box(_position - extents, _position + extents); -} - -int PaintHeightfieldMaterialEditVisitor::visit(MetavoxelInfo& info) { - if (!info.getBounds().intersects(_bounds)) { - return STOP_RECURSION; - } - if (!info.isLeaf) { - return DEFAULT_ORDER; - } - HeightfieldColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue(); - if (colorPointer) { - QByteArray contents(colorPointer->getContents()); - int size = glm::sqrt((float)contents.size() / DataBlock::COLOR_BYTES); - int highest = size - 1; - float heightScale = size / info.size; - - glm::vec3 center = (_position - info.minimum) * heightScale; - float scaledRadius = _radius * heightScale; - glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius); - - glm::vec3 start = glm::floor(center - extents); - glm::vec3 end = glm::ceil(center + extents); - - // paint all points within the radius - float z = qMax(start.z, 0.0f); - float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest); - int stride = size * DataBlock::COLOR_BYTES; - char* lineDest = contents.data() + (int)z * stride + (int)startX * DataBlock::COLOR_BYTES; - float squaredRadius = scaledRadius * scaledRadius; - char red = _color.red(), green = _color.green(), blue = _color.blue(); - bool changed = false; - for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) { - char* dest = lineDest; - for (float x = startX; x <= endX; x += 1.0f, dest += DataBlock::COLOR_BYTES) { - float dx = x - center.x, dz = z - center.z; - if (dx * dx + dz * dz <= squaredRadius) { - dest[0] = red; - dest[1] = green; - dest[2] = blue; - changed = true; - } - } - lineDest += stride; - } - if (changed) { - HeightfieldColorDataPointer newPointer(new HeightfieldColorData(contents)); - info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(), - encodeInline(newPointer)); - } - } - - HeightfieldMaterialDataPointer materialPointer = info.inputValues.at(1).getInlineValue(); - if (materialPointer) { - QVector materials = materialPointer->getMaterials(); - QByteArray contents(materialPointer->getContents()); - uchar materialIndex = getMaterialIndex(_material, materials, contents); - int size = glm::sqrt((float)contents.size()); - int highest = size - 1; - float heightScale = highest / info.size; - - glm::vec3 center = (_position - info.minimum) * heightScale; - float scaledRadius = _radius * heightScale; - glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius); - - glm::vec3 start = glm::floor(center - extents); - glm::vec3 end = glm::ceil(center + extents); - - // paint all points within the radius - float z = qMax(start.z, 0.0f); - float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest); - uchar* lineDest = (uchar*)contents.data() + (int)z * size + (int)startX; - float squaredRadius = scaledRadius * scaledRadius; - bool changed = false; - for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) { - uchar* dest = lineDest; - for (float x = startX; x <= endX; x += 1.0f, dest++) { - float dx = x - center.x, dz = z - center.z; - if (dx * dx + dz * dz <= squaredRadius) { - *dest = materialIndex; - changed = true; - } - } - lineDest += size; - } - if (changed) { - clearUnusedMaterials(materials, contents); - HeightfieldMaterialDataPointer newPointer(new HeightfieldMaterialData(contents, materials)); - info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline(newPointer)); - } - } - return STOP_RECURSION; -} - PaintHeightfieldMaterialEdit::PaintHeightfieldMaterialEdit(const glm::vec3& position, float radius, const SharedObjectPointer& material, const QColor& averageColor) : MaterialEdit(material, averageColor), @@ -549,8 +183,17 @@ PaintHeightfieldMaterialEdit::PaintHeightfieldMaterialEdit(const glm::vec3& posi } void PaintHeightfieldMaterialEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - PaintHeightfieldMaterialEditVisitor visitor(position, radius, material, averageColor); - data.guide(visitor); + glm::vec3 extents(radius, radius, radius); + QVector results; + data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(), + Box(position - extents, position + extents), results); + + foreach (const SharedObjectPointer& spanner, results) { + Spanner* newSpanner = static_cast(spanner.data())->paintMaterial(position, radius, material, averageColor); + if (newSpanner != spanner) { + data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), spanner, newSpanner); + } + } } const int VOXEL_BLOCK_SIZE = 16; @@ -709,7 +352,8 @@ int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) { if (minZ > 0) { hermiteMinZ--; hermiteSizeZ++; - } + } + const int EIGHT_BIT_MAXIMUM = 255; QRgb* hermiteDestZ = hermiteContents.data() + hermiteMinZ * hermiteArea + hermiteMinY * hermiteSamples + hermiteMinX * VoxelHermiteData::EDGE_COUNT; for (int z = hermiteMinZ, hermiteMaxZ = z + hermiteSizeZ - 1; z <= hermiteMaxZ; z++, hermiteDestZ += hermiteArea) { @@ -850,262 +494,6 @@ int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) { return STOP_RECURSION; } -class HeightfieldClearFetchVisitor : public MetavoxelVisitor { -public: - - HeightfieldClearFetchVisitor(const Box& bounds, float granularity); - - const SharedObjectPointer& getSpanner() const { return _spanner; } - - virtual int visit(MetavoxelInfo& info); - -private: - - Box _bounds; - Box _expandedBounds; - SharedObjectPointer _spanner; - Box _spannerBounds; - int _heightfieldWidth; - int _heightfieldHeight; -}; - -HeightfieldClearFetchVisitor::HeightfieldClearFetchVisitor(const Box& bounds, float granularity) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute() << - AttributeRegistry::getInstance()->getHeightfieldColorAttribute() << - AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), QVector() << - AttributeRegistry::getInstance()->getHeightfieldAttribute() << - AttributeRegistry::getInstance()->getHeightfieldColorAttribute() << - AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute()) { - - // find the bounds of all voxel nodes intersected - float nodeSize = VOXEL_BLOCK_SIZE * glm::pow(2.0f, glm::floor(glm::log(granularity) / glm::log(2.0f))); - _bounds.minimum = glm::floor(bounds.minimum / nodeSize) * nodeSize; - _bounds.maximum = glm::ceil(bounds.maximum / nodeSize) * nodeSize; - - // expand to include edges - _expandedBounds = _bounds; - float increment = nodeSize / VOXEL_BLOCK_SIZE; - _expandedBounds.maximum.x += increment; - _expandedBounds.maximum.z += increment; -} - -int HeightfieldClearFetchVisitor::visit(MetavoxelInfo& info) { - Box bounds = info.getBounds(); - if (!bounds.intersects(_expandedBounds)) { - return STOP_RECURSION; - } - if (!info.isLeaf) { - return DEFAULT_ORDER; - } - HeightfieldHeightDataPointer heightPointer = info.inputValues.at(0).getInlineValue(); - if (!heightPointer) { - return STOP_RECURSION; - } - QByteArray contents(heightPointer->getContents()); - int size = glm::sqrt((float)contents.size()); - float heightScale = size / info.size; - - Box overlap = bounds.getIntersection(_expandedBounds); - int srcX = (overlap.minimum.x - info.minimum.x) * heightScale; - int srcY = (overlap.minimum.z - info.minimum.z) * heightScale; - int srcWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) * heightScale); - int srcHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) * heightScale); - char* src = contents.data() + srcY * size + srcX; - - // check for non-zero values - bool foundNonZero = false; - for (int y = 0; y < srcHeight; y++, src += (size - srcWidth)) { - for (char* end = src + srcWidth; src != end; src++) { - if (*src != 0) { - foundNonZero = true; - goto outerBreak; - } - } - } - outerBreak: - - // if everything is zero, we're done - if (!foundNonZero) { - return STOP_RECURSION; - } - - // create spanner if necessary - Heightfield* spanner = static_cast(_spanner.data()); - float increment = 1.0f / heightScale; - if (!spanner) { - _spannerBounds.minimum = glm::floor(_bounds.minimum / increment) * increment; - _spannerBounds.maximum = (glm::ceil(_bounds.maximum / increment) + glm::vec3(1.0f, 0.0f, 1.0f)) * increment; - _spannerBounds.minimum.y = bounds.minimum.y; - _spannerBounds.maximum.y = bounds.maximum.y; - _heightfieldWidth = (int)glm::round((_spannerBounds.maximum.x - _spannerBounds.minimum.x) / increment); - _heightfieldHeight = (int)glm::round((_spannerBounds.maximum.z - _spannerBounds.minimum.z) / increment); - int heightfieldArea = _heightfieldWidth * _heightfieldHeight; - Box innerBounds = _spannerBounds; - innerBounds.maximum.x -= increment; - innerBounds.maximum.z -= increment; - _spanner = spanner = new Heightfield(innerBounds, increment, QByteArray(heightfieldArea, 0), - QByteArray(heightfieldArea * DataBlock::COLOR_BYTES, 0), QByteArray(heightfieldArea, 0), - QVector()); - } - - // copy the inner area - overlap = bounds.getIntersection(_spannerBounds); - int destX = (overlap.minimum.x - _spannerBounds.minimum.x) * heightScale; - int destY = (overlap.minimum.z - _spannerBounds.minimum.z) * heightScale; - int destWidth = (int)glm::round((overlap.maximum.x - overlap.minimum.x) * heightScale); - int destHeight = (int)glm::round((overlap.maximum.z - overlap.minimum.z) * heightScale); - char* dest = spanner->getHeight().data() + destY * _heightfieldWidth + destX; - srcX = (overlap.minimum.x - info.minimum.x) * heightScale; - srcY = (overlap.minimum.z - info.minimum.z) * heightScale; - src = contents.data() + srcY * size + srcX; - - for (int y = 0; y < destHeight; y++, dest += _heightfieldWidth, src += size) { - memcpy(dest, src, destWidth); - } - - // clear the inner area - Box innerBounds = _spannerBounds; - innerBounds.minimum.x += increment; - innerBounds.minimum.z += increment; - innerBounds.maximum.x -= increment; - innerBounds.maximum.z -= increment; - Box innerOverlap = bounds.getIntersection(innerBounds); - destX = (innerOverlap.minimum.x - info.minimum.x) * heightScale; - destY = (innerOverlap.minimum.z - info.minimum.z) * heightScale; - destWidth = glm::ceil((innerOverlap.maximum.x - innerOverlap.minimum.x) * heightScale); - destHeight = glm::ceil((innerOverlap.maximum.z - innerOverlap.minimum.z) * heightScale); - dest = contents.data() + destY * size + destX; - - for (int y = 0; y < destHeight; y++, dest += size) { - memset(dest, 0, destWidth); - } - - // see if there are any non-zero values left - foundNonZero = false; - dest = contents.data(); - for (char* end = dest + contents.size(); dest != end; dest++) { - if (*dest != 0) { - foundNonZero = true; - break; - } - } - - // if all is gone, clear the node - if (foundNonZero) { - HeightfieldHeightDataPointer newHeightPointer(new HeightfieldHeightData(contents)); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(newHeightPointer)); - - } else { - info.outputValues[0] = AttributeValue(_outputs.at(0)); - } - - // allow a border for what we clear in terms of color/material - innerBounds.minimum.x += increment; - innerBounds.minimum.z += increment; - innerBounds.maximum.x -= increment; - innerBounds.maximum.z -= increment; - innerOverlap = bounds.getIntersection(innerBounds); - - HeightfieldColorDataPointer colorPointer = info.inputValues.at(1).getInlineValue(); - if (colorPointer) { - contents = colorPointer->getContents(); - size = glm::sqrt((float)contents.size() / DataBlock::COLOR_BYTES); - heightScale = size / info.size; - - // copy the inner area - destX = (overlap.minimum.x - _spannerBounds.minimum.x) * heightScale; - destY = (overlap.minimum.z - _spannerBounds.minimum.z) * heightScale; - destWidth = (int)glm::round((overlap.maximum.x - overlap.minimum.x) * heightScale); - destHeight = (int)glm::round((overlap.maximum.z - overlap.minimum.z) * heightScale); - dest = spanner->getColor().data() + (destY * _heightfieldWidth + destX) * DataBlock::COLOR_BYTES; - srcX = (overlap.minimum.x - info.minimum.x) * heightScale; - srcY = (overlap.minimum.z - info.minimum.z) * heightScale; - src = contents.data() + (srcY * size + srcX) * DataBlock::COLOR_BYTES; - - for (int y = 0; y < destHeight; y++, dest += _heightfieldWidth * DataBlock::COLOR_BYTES, - src += size * DataBlock::COLOR_BYTES) { - memcpy(dest, src, destWidth * DataBlock::COLOR_BYTES); - } - - if (foundNonZero) { - destX = (innerOverlap.minimum.x - info.minimum.x) * heightScale; - destY = (innerOverlap.minimum.z - info.minimum.z) * heightScale; - destWidth = glm::ceil((innerOverlap.maximum.x - innerOverlap.minimum.x) * heightScale); - destHeight = glm::ceil((innerOverlap.maximum.z - innerOverlap.minimum.z) * heightScale); - if (destWidth > 0 && destHeight > 0) { - dest = contents.data() + (destY * size + destX) * DataBlock::COLOR_BYTES; - - for (int y = 0; y < destHeight; y++, dest += size * DataBlock::COLOR_BYTES) { - memset(dest, 0, destWidth * DataBlock::COLOR_BYTES); - } - - HeightfieldColorDataPointer newColorPointer(new HeightfieldColorData(contents)); - info.outputValues[1] = AttributeValue(_outputs.at(1), - encodeInline(newColorPointer)); - } - } else { - info.outputValues[1] = AttributeValue(_outputs.at(1)); - } - } - - HeightfieldMaterialDataPointer materialPointer = info.inputValues.at(2).getInlineValue(); - if (materialPointer) { - contents = materialPointer->getContents(); - QVector materials = materialPointer->getMaterials(); - size = glm::sqrt((float)contents.size()); - heightScale = size / info.size; - - // copy the inner area - destX = (overlap.minimum.x - _spannerBounds.minimum.x) * heightScale; - destY = (overlap.minimum.z - _spannerBounds.minimum.z) * heightScale; - destWidth = (int)glm::round((overlap.maximum.x - overlap.minimum.x) * heightScale); - destHeight = (int)glm::round((overlap.maximum.z - overlap.minimum.z) * heightScale); - uchar* dest = (uchar*)spanner->getMaterial().data() + destY * _heightfieldWidth + destX; - srcX = (overlap.minimum.x - info.minimum.x) * heightScale; - srcY = (overlap.minimum.z - info.minimum.z) * heightScale; - uchar* src = (uchar*)contents.data() + srcY * size + srcX; - QHash materialMap; - - for (int y = 0; y < destHeight; y++, dest += _heightfieldWidth, src += size) { - for (uchar* lineSrc = src, *lineDest = dest, *end = src + destWidth; lineSrc != end; lineSrc++, lineDest++) { - int material = *lineSrc; - if (material != 0) { - int& mapping = materialMap[material]; - if (mapping == 0) { - mapping = getMaterialIndex(materials.at(material - 1), spanner->getMaterials(), - spanner->getMaterial()); - } - material = mapping; - } - *lineDest = material; - } - } - - if (foundNonZero) { - destX = (innerOverlap.minimum.x - info.minimum.x) * heightScale; - destY = (innerOverlap.minimum.z - info.minimum.z) * heightScale; - destWidth = glm::ceil((innerOverlap.maximum.x - innerOverlap.minimum.x) * heightScale); - destHeight = glm::ceil((innerOverlap.maximum.z - innerOverlap.minimum.z) * heightScale); - if (destWidth > 0 && destHeight > 0) { - dest = (uchar*)contents.data() + destY * size + destX; - - for (int y = 0; y < destHeight; y++, dest += size) { - memset(dest, 0, destWidth); - } - - clearUnusedMaterials(materials, contents); - HeightfieldMaterialDataPointer newMaterialPointer(new HeightfieldMaterialData(contents, materials)); - info.outputValues[2] = AttributeValue(_outputs.at(2), - encodeInline(newMaterialPointer)); - } - } else { - info.outputValues[2] = AttributeValue(_outputs.at(2)); - } - } - - return STOP_RECURSION; -} - void VoxelMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { // expand to fit the entire edit Spanner* spanner = static_cast(this->spanner.data()); @@ -1116,14 +504,34 @@ void VoxelMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObject QColor color = averageColor; color.setAlphaF(color.alphaF() > 0.5f ? 1.0f : 0.0f); - // clear/fetch any heightfield data - HeightfieldClearFetchVisitor heightfieldVisitor(spanner->getBounds(), spanner->getVoxelizationGranularity()); - data.guide(heightfieldVisitor); + // find the bounds of all voxel nodes intersected + float nodeSize = VOXEL_BLOCK_SIZE * glm::pow(2.0f, glm::floor( + glm::log(spanner->getVoxelizationGranularity()) / glm::log(2.0f))); + Box bounds(glm::floor(spanner->getBounds().minimum / nodeSize) * nodeSize, + glm::ceil(spanner->getBounds().maximum / nodeSize) * nodeSize); + + // expand to include edges + Box expandedBounds = bounds; + float increment = nodeSize / VOXEL_BLOCK_SIZE; + expandedBounds.maximum.x += increment; + expandedBounds.maximum.z += increment; + + // get all intersecting spanners + QVector results; + data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(), expandedBounds, results); + + // clear/voxelize as appropriate + SharedObjectPointer heightfield; + foreach (const SharedObjectPointer& result, results) { + Spanner* newSpanner = static_cast(result.data())->clearAndFetchHeight(bounds, heightfield); + if (newSpanner != result) { + data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), result, newSpanner); + } + } // voxelize the fetched heightfield, if any - if (heightfieldVisitor.getSpanner()) { - VoxelMaterialSpannerEditVisitor visitor(static_cast(heightfieldVisitor.getSpanner().data()), - material, color); + if (heightfield) { + VoxelMaterialSpannerEditVisitor visitor(static_cast(heightfield.data()), material, color); data.guide(visitor); } diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 06f2fc9b8d..10477e0486 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -177,21 +177,6 @@ public: DECLARE_STREAMABLE_METATYPE(ClearSpannersEdit) -/// An edit that sets a spanner's attributes in the voxel tree. -class SetSpannerEdit : public MetavoxelEdit { - STREAMABLE - -public: - - STREAM SharedObjectPointer spanner; - - SetSpannerEdit(const SharedObjectPointer& spanner = SharedObjectPointer()); - - virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; -}; - -DECLARE_STREAMABLE_METATYPE(SetSpannerEdit) - /// An edit that directly sets part of the metavoxel data. class SetDataEdit : public MetavoxelEdit { STREAMABLE diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index 4aff339738..cdeec61500 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -77,32 +76,11 @@ QByteArray DelegatingItemEditorFactory::valuePropertyName(int userType) const { return propertyName.isNull() ? _parentFactory->valuePropertyName(userType) : propertyName; } -static QItemEditorFactory* getItemEditorFactory() { +QItemEditorFactory* getItemEditorFactory() { static QItemEditorFactory* factory = new DelegatingItemEditorFactory(); return factory; } -/// Because Windows doesn't necessarily have the staticMetaObject available when we want to create, -/// this class simply delays the value property name lookup until actually requested. -template class LazyItemEditorCreator : public QItemEditorCreatorBase { -public: - - virtual QWidget* createWidget(QWidget* parent) const { return new T(parent); } - - virtual QByteArray valuePropertyName() const; - -protected: - - QByteArray _valuePropertyName; -}; - -template QByteArray LazyItemEditorCreator::valuePropertyName() const { - if (_valuePropertyName.isNull()) { - const_cast*>(this)->_valuePropertyName = T::staticMetaObject.userProperty().name(); - } - return _valuePropertyName; -} - static QItemEditorCreatorBase* createDoubleEditorCreator() { QItemEditorCreatorBase* creator = new LazyItemEditorCreator(); getItemEditorFactory()->registerEditor(qMetaTypeId(), creator); diff --git a/libraries/metavoxels/src/MetavoxelUtil.h b/libraries/metavoxels/src/MetavoxelUtil.h index 4228af059f..3be54ca60d 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.h +++ b/libraries/metavoxels/src/MetavoxelUtil.h @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -24,6 +25,7 @@ class QByteArray; class QDoubleSpinBox; +class QItemEditorFactory; class QPushButton; class NetworkProgram; @@ -108,6 +110,30 @@ private: AxisExtents _crossProductExtents[CROSS_PRODUCT_EXTENT_COUNT]; }; +/// Returns a pointer to the singleton item editor factory. +QItemEditorFactory* getItemEditorFactory(); + +/// Because Windows doesn't necessarily have the staticMetaObject available when we want to create, +/// this class simply delays the value property name lookup until actually requested. +template class LazyItemEditorCreator : public QItemEditorCreatorBase { +public: + + virtual QWidget* createWidget(QWidget* parent) const { return new T(parent); } + + virtual QByteArray valuePropertyName() const; + +protected: + + QByteArray _valuePropertyName; +}; + +template QByteArray LazyItemEditorCreator::valuePropertyName() const { + if (_valuePropertyName.isNull()) { + const_cast*>(this)->_valuePropertyName = T::staticMetaObject.userProperty().name(); + } + return _valuePropertyName; +} + /// Editor for meta-object values. class QMetaObjectEditor : public QWidget { Q_OBJECT diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp new file mode 100644 index 0000000000..617d753414 --- /dev/null +++ b/libraries/metavoxels/src/Spanner.cpp @@ -0,0 +1,2126 @@ +// +// 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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "Spanner.h" + +using namespace std; + +REGISTER_META_OBJECT(Spanner) +REGISTER_META_OBJECT(Heightfield) +REGISTER_META_OBJECT(Sphere) +REGISTER_META_OBJECT(Cuboid) +REGISTER_META_OBJECT(StaticModel) + +static int heightfieldHeightTypeId = registerSimpleMetaType(); +static int heightfieldColorTypeId = registerSimpleMetaType(); +static int heightfieldMaterialTypeId = registerSimpleMetaType(); + +static QItemEditorCreatorBase* createHeightfieldHeightEditorCreator() { + QItemEditorCreatorBase* creator = new LazyItemEditorCreator(); + getItemEditorFactory()->registerEditor(qMetaTypeId(), creator); + return creator; +} + +static QItemEditorCreatorBase* createHeightfieldColorEditorCreator() { + QItemEditorCreatorBase* creator = new LazyItemEditorCreator(); + getItemEditorFactory()->registerEditor(qMetaTypeId(), creator); + return creator; +} + +static QItemEditorCreatorBase* heightfieldHeightEditorCreator = createHeightfieldHeightEditorCreator(); +static QItemEditorCreatorBase* heightfieldColorEditorCreator = createHeightfieldColorEditorCreator(); + +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(metaObject->newInstance()); + connect(this, &QObject::destroyed, _renderer, &QObject::deleteLater); + _renderer->init(this); + } + return _renderer; +} + +bool Spanner::isHeightfield() const { + return false; +} + +float Spanner::getHeight(const glm::vec3& location) const { + return -FLT_MAX; +} + +bool Spanner::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { + return _bounds.findRayIntersection(origin, direction, distance); +} + +Spanner* Spanner::paintMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material, + const QColor& color) { + return this; +} + +Spanner* Spanner::paintHeight(const glm::vec3& position, float radius, float height) { + return this; +} + +Spanner* Spanner::clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield) { + return this; +} + +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& Spanner::getMaterials() { + static QVector 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(bool cursor) { + // 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 int HeightfieldData::SHARED_EDGE = 1; + +HeightfieldData::HeightfieldData(int width) : + _width(width) { +} + +const int HEIGHTFIELD_DATA_HEADER_SIZE = sizeof(qint32) * 4; + +static QByteArray encodeHeightfieldHeight(int offsetX, int offsetY, int width, int height, const QVector& contents) { + QByteArray inflated(HEIGHTFIELD_DATA_HEADER_SIZE, 0); + qint32* header = (qint32*)inflated.data(); + *header++ = offsetX; + *header++ = offsetY; + *header++ = width; + *header++ = height; + if (!contents.isEmpty()) { + // encode with Paeth filter (see http://en.wikipedia.org/wiki/Portable_Network_Graphics#Filtering) + QVector filteredContents(contents.size()); + const quint16* src = contents.constData(); + quint16* dest = filteredContents.data(); + *dest++ = *src++; + for (quint16* end = dest + width - 1; dest != end; dest++, src++) { + *dest = *src - src[-1]; + } + for (int y = 1; y < height; y++) { + *dest++ = *src - src[-width]; + src++; + for (quint16* end = dest + width - 1; dest != end; dest++, src++) { + int a = src[-1]; + int b = src[-width]; + int c = src[-width - 1]; + int p = a + b - c; + int ad = abs(a - p); + int bd = abs(b - p); + int cd = abs(c - p); + *dest = *src - (ad < bd ? (ad < cd ? a : c) : (bd < cd ? b : c)); + } + } + inflated.append((const char*)filteredContents.constData(), filteredContents.size() * sizeof(quint16)); + } + return qCompress(inflated); +} + +static QVector decodeHeightfieldHeight(const QByteArray& encoded, int& offsetX, int& offsetY, + int& width, int& height) { + QByteArray inflated = qUncompress(encoded); + const qint32* header = (const qint32*)inflated.constData(); + offsetX = *header++; + offsetY = *header++; + width = *header++; + height = *header++; + int payloadSize = inflated.size() - HEIGHTFIELD_DATA_HEADER_SIZE; + QVector unfiltered(payloadSize / sizeof(quint16)); + if (!unfiltered.isEmpty()) { + quint16* dest = unfiltered.data(); + const quint16* src = (const quint16*)(inflated.constData() + HEIGHTFIELD_DATA_HEADER_SIZE); + *dest++ = *src++; + for (quint16* end = dest + width - 1; dest != end; dest++, src++) { + *dest = *src + dest[-1]; + } + for (int y = 1; y < height; y++) { + *dest = (*src++) + dest[-width]; + dest++; + for (quint16* end = dest + width - 1; dest != end; dest++, src++) { + int a = dest[-1]; + int b = dest[-width]; + int c = dest[-width - 1]; + int p = a + b - c; + int ad = abs(a - p); + int bd = abs(b - p); + int cd = abs(c - p); + *dest = *src + (ad < bd ? (ad < cd ? a : c) : (bd < cd ? b : c)); + } + } + } + return unfiltered; +} + +const int HeightfieldHeight::HEIGHT_BORDER = 1; +const int HeightfieldHeight::HEIGHT_EXTENSION = SHARED_EDGE + 2 * HEIGHT_BORDER; + +HeightfieldHeight::HeightfieldHeight(int width, const QVector& contents) : + HeightfieldData(width), + _contents(contents) { +} + +HeightfieldHeight::HeightfieldHeight(Bitstream& in, int bytes) { + read(in, bytes); +} + +HeightfieldHeight::HeightfieldHeight(Bitstream& in, int bytes, const HeightfieldHeightPointer& reference) { + if (!reference) { + read(in, bytes); + return; + } + QMutexLocker locker(&reference->getEncodedDeltaMutex()); + reference->setEncodedDelta(in.readAligned(bytes)); + reference->setDeltaData(DataBlockPointer(this)); + _width = reference->getWidth(); + _contents = reference->getContents(); + + int offsetX, offsetY, width, height; + QVector delta = decodeHeightfieldHeight(reference->getEncodedDelta(), offsetX, offsetY, width, height); + if (delta.isEmpty()) { + return; + } + if (offsetX == 0) { + _contents = delta; + _width = width; + return; + } + int minX = offsetX - 1; + int minY = offsetY - 1; + const quint16* src = delta.constData(); + quint16* dest = _contents.data() + minY * _width + minX; + for (int y = 0; y < height; y++, src += width, dest += _width) { + memcpy(dest, src, width * sizeof(quint16)); + } +} + +void HeightfieldHeight::write(Bitstream& out) { + QMutexLocker locker(&_encodedMutex); + if (_encoded.isEmpty()) { + _encoded = encodeHeightfieldHeight(0, 0, _width, _contents.size() / _width, _contents); + } + out << _encoded.size(); + out.writeAligned(_encoded); +} + +void HeightfieldHeight::writeDelta(Bitstream& out, const HeightfieldHeightPointer& reference) { + if (!reference || reference->getWidth() != _width || reference->getContents().size() != _contents.size()) { + write(out); + return; + } + QMutexLocker locker(&reference->getEncodedDeltaMutex()); + if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { + int height = _contents.size() / _width; + int minX = _width, minY = height; + int maxX = -1, maxY = -1; + const quint16* src = _contents.constData(); + const quint16* ref = reference->getContents().constData(); + for (int y = 0; y < height; y++) { + bool difference = false; + for (int x = 0; x < _width; x++) { + if (*src++ != *ref++) { + minX = qMin(minX, x); + maxX = qMax(maxX, x); + difference = true; + } + } + if (difference) { + minY = qMin(minY, y); + maxY = qMax(maxY, y); + } + } + QVector delta; + int deltaWidth = 0, deltaHeight = 0; + if (maxX >= minX) { + deltaWidth = maxX - minX + 1; + deltaHeight = maxY - minY + 1; + delta = QVector(deltaWidth * deltaHeight); + quint16* dest = delta.data(); + src = _contents.constData() + minY * _width + minX; + for (int y = 0; y < deltaHeight; y++, src += _width, dest += deltaWidth) { + memcpy(dest, src, deltaWidth * sizeof(quint16)); + } + } + reference->setEncodedDelta(encodeHeightfieldHeight(minX + 1, minY + 1, deltaWidth, deltaHeight, delta)); + reference->setDeltaData(DataBlockPointer(this)); + } + out << reference->getEncodedDelta().size(); + out.writeAligned(reference->getEncodedDelta()); +} + +void HeightfieldHeight::read(Bitstream& in, int bytes) { + int offsetX, offsetY, height; + _contents = decodeHeightfieldHeight(_encoded = in.readAligned(bytes), offsetX, offsetY, _width, height); +} + +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(in, size); + } + return in; +} + +template<> void Bitstream::writeRawDelta(const HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference) { + if (value) { + value->writeDelta(*this, reference); + } else { + *this << 0; + } +} + +template<> void Bitstream::readRawDelta(HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference) { + int size; + *this >> size; + if (size == 0) { + value = HeightfieldHeightPointer(); + } else { + value = new HeightfieldHeight(*this, size, reference); + } +} + +HeightfieldHeightEditor::HeightfieldHeightEditor(QWidget* parent) : + QWidget(parent) { + + QHBoxLayout* layout = new QHBoxLayout(); + setLayout(layout); + + layout->addWidget(_select = new QPushButton("Select")); + connect(_select, &QPushButton::clicked, this, &HeightfieldHeightEditor::select); + + layout->addWidget(_clear = new QPushButton("Clear")); + connect(_clear, &QPushButton::clicked, this, &HeightfieldHeightEditor::clear); + _clear->setEnabled(false); +} + +void HeightfieldHeightEditor::setHeight(const HeightfieldHeightPointer& height) { + if ((_height = height)) { + _clear->setEnabled(true); + } else { + _clear->setEnabled(false); + } +} + +static int getHeightfieldSize(int size) { + return (int)glm::pow(2.0f, glm::round(glm::log((float)size - HeightfieldData::SHARED_EDGE) / + glm::log(2.0f))) + HeightfieldData::SHARED_EDGE; +} + +void HeightfieldHeightEditor::select() { + QSettings settings; + QString result = QFileDialog::getOpenFileName(this, "Select Height Image", settings.value("heightDir").toString(), + "Images (*.png *.jpg *.bmp *.raw)"); + if (result.isNull()) { + return; + } + settings.setValue("heightDir", QFileInfo(result).path()); + const quint16 CONVERSION_OFFSET = 1; + if (result.toLower().endsWith(".raw")) { + QFile input(result); + input.open(QIODevice::ReadOnly); + QDataStream in(&input); + in.setByteOrder(QDataStream::LittleEndian); + QVector rawContents; + while (!in.atEnd()) { + quint16 height; + in >> height; + rawContents.append(height); + } + if (rawContents.isEmpty()) { + QMessageBox::warning(this, "Invalid Image", "The selected image could not be read."); + return; + } + int rawSize = glm::sqrt((float)rawContents.size()); + int size = getHeightfieldSize(rawSize) + 2 * HeightfieldHeight::HEIGHT_BORDER; + QVector contents(size * size); + quint16* dest = contents.data() + (size + 1) * HeightfieldHeight::HEIGHT_BORDER; + const quint16* src = rawContents.constData(); + const float CONVERSION_SCALE = 65534.0f / numeric_limits::max(); + for (int i = 0; i < rawSize; i++, dest += size) { + for (quint16* lineDest = dest, *end = dest + rawSize; lineDest != end; lineDest++, src++) { + *lineDest = (quint16)(*src * CONVERSION_SCALE) + CONVERSION_OFFSET; + } + } + emit heightChanged(_height = new HeightfieldHeight(size, contents)); + _clear->setEnabled(true); + return; + } + QImage image; + if (!image.load(result)) { + QMessageBox::warning(this, "Invalid Image", "The selected image could not be read."); + return; + } + image = image.convertToFormat(QImage::Format_RGB888); + int width = getHeightfieldSize(image.width()) + 2 * HeightfieldHeight::HEIGHT_BORDER; + int height = getHeightfieldSize(image.height()) + 2 * HeightfieldHeight::HEIGHT_BORDER; + QVector contents(width * height); + quint16* dest = contents.data() + (width + 1) * HeightfieldHeight::HEIGHT_BORDER; + const float CONVERSION_SCALE = 65534.0f / numeric_limits::max(); + for (int i = 0; i < image.height(); i++, dest += width) { + const uchar* src = image.constScanLine(i); + for (quint16* lineDest = dest, *end = dest + image.width(); lineDest != end; lineDest++, + src += DataBlock::COLOR_BYTES) { + *lineDest = (quint16)(*src * CONVERSION_SCALE) + CONVERSION_OFFSET; + } + } + emit heightChanged(_height = new HeightfieldHeight(width, contents)); + _clear->setEnabled(true); +} + +void HeightfieldHeightEditor::clear() { + emit heightChanged(_height = HeightfieldHeightPointer()); + _clear->setEnabled(false); +} + +static QByteArray encodeHeightfieldColor(int offsetX, int offsetY, int width, int height, const QByteArray& contents) { + QByteArray inflated(HEIGHTFIELD_DATA_HEADER_SIZE + contents.size(), 0); + qint32* header = (qint32*)inflated.data(); + *header++ = offsetX; + *header++ = offsetY; + *header++ = width; + *header++ = height; + if (!contents.isEmpty()) { + // encode with Paeth filter (see http://en.wikipedia.org/wiki/Portable_Network_Graphics#Filtering) + const uchar* src = (const uchar*)contents.constData(); + uchar* dest = (uchar*)inflated.data() + HEIGHTFIELD_DATA_HEADER_SIZE; + *dest++ = *src++; + *dest++ = *src++; + *dest++ = *src++; + int stride = width * DataBlock::COLOR_BYTES; + for (uchar* end = dest + stride - DataBlock::COLOR_BYTES; dest != end; dest++, src++) { + *dest = *src - src[-DataBlock::COLOR_BYTES]; + } + for (int y = 1; y < height; y++) { + *dest++ = *src - src[-stride]; + src++; + *dest++ = *src - src[-stride]; + src++; + *dest++ = *src - src[-stride]; + src++; + for (uchar* end = dest + stride - DataBlock::COLOR_BYTES; dest != end; dest++, src++) { + int a = src[-DataBlock::COLOR_BYTES]; + int b = src[-stride]; + int c = src[-stride - DataBlock::COLOR_BYTES]; + int p = a + b - c; + int ad = abs(a - p); + int bd = abs(b - p); + int cd = abs(c - p); + *dest = *src - (ad < bd ? (ad < cd ? a : c) : (bd < cd ? b : c)); + } + } + } + return qCompress(inflated); +} + +static QByteArray decodeHeightfieldColor(const QByteArray& encoded, int& offsetX, int& offsetY, int& width, int& height) { + QByteArray inflated = qUncompress(encoded); + const qint32* header = (const qint32*)inflated.constData(); + offsetX = *header++; + offsetY = *header++; + width = *header++; + height = *header++; + QByteArray contents(inflated.size() - HEIGHTFIELD_DATA_HEADER_SIZE, 0); + if (!contents.isEmpty()) { + const uchar* src = (const uchar*)inflated.constData() + HEIGHTFIELD_DATA_HEADER_SIZE; + uchar* dest = (uchar*)contents.data(); + *dest++ = *src++; + *dest++ = *src++; + *dest++ = *src++; + int stride = width * DataBlock::COLOR_BYTES; + for (uchar* end = dest + stride - DataBlock::COLOR_BYTES; dest != end; dest++, src++) { + *dest = *src + dest[-DataBlock::COLOR_BYTES]; + } + for (int y = 1; y < height; y++) { + *dest = (*src++) + dest[-stride]; + dest++; + *dest = (*src++) + dest[-stride]; + dest++; + *dest = (*src++) + dest[-stride]; + dest++; + for (uchar* end = dest + stride - DataBlock::COLOR_BYTES; dest != end; dest++, src++) { + int a = dest[-DataBlock::COLOR_BYTES]; + int b = dest[-stride]; + int c = dest[-stride - DataBlock::COLOR_BYTES]; + int p = a + b - c; + int ad = abs(a - p); + int bd = abs(b - p); + int cd = abs(c - p); + *dest = *src + (ad < bd ? (ad < cd ? a : c) : (bd < cd ? b : c)); + } + } + } + return contents; +} + +HeightfieldColor::HeightfieldColor(int width, const QByteArray& contents) : + HeightfieldData(width), + _contents(contents) { +} + +HeightfieldColor::HeightfieldColor(Bitstream& in, int bytes) { + read(in, bytes); +} + +HeightfieldColor::HeightfieldColor(Bitstream& in, int bytes, const HeightfieldColorPointer& reference) { + if (!reference) { + read(in, bytes); + return; + } + QMutexLocker locker(&reference->getEncodedDeltaMutex()); + reference->setEncodedDelta(in.readAligned(bytes)); + reference->setDeltaData(DataBlockPointer(this)); + _width = reference->getWidth(); + _contents = reference->getContents(); + + int offsetX, offsetY, width, height; + QByteArray delta = decodeHeightfieldColor(reference->getEncodedDelta(), offsetX, offsetY, width, height); + if (delta.isEmpty()) { + return; + } + if (offsetX == 0) { + _contents = delta; + _width = width; + return; + } + int minX = offsetX - 1; + int minY = offsetY - 1; + const char* src = delta.constData(); + char* dest = _contents.data() + (minY * _width + minX) * DataBlock::COLOR_BYTES; + for (int y = 0; y < height; y++, src += width * DataBlock::COLOR_BYTES, dest += _width * DataBlock::COLOR_BYTES) { + memcpy(dest, src, width * DataBlock::COLOR_BYTES); + } +} + +void HeightfieldColor::write(Bitstream& out) { + QMutexLocker locker(&_encodedMutex); + if (_encoded.isEmpty()) { + _encoded = encodeHeightfieldColor(0, 0, _width, _contents.size() / (_width * DataBlock::COLOR_BYTES), _contents); + } + out << _encoded.size(); + out.writeAligned(_encoded); +} + +void HeightfieldColor::writeDelta(Bitstream& out, const HeightfieldColorPointer& reference) { + if (!reference || reference->getWidth() != _width || reference->getContents().size() != _contents.size()) { + write(out); + return; + } + QMutexLocker locker(&reference->getEncodedDeltaMutex()); + if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { + int height = _contents.size() / (_width * DataBlock::COLOR_BYTES); + int minX = _width, minY = height; + int maxX = -1, maxY = -1; + const char* src = _contents.constData(); + const char* ref = reference->getContents().constData(); + for (int y = 0; y < height; y++) { + bool difference = false; + for (int x = 0; x < _width; x++, src += DataBlock::COLOR_BYTES, ref += DataBlock::COLOR_BYTES) { + if (src[0] != ref[0] || src[1] != ref[1] || src[2] != ref[2]) { + minX = qMin(minX, x); + maxX = qMax(maxX, x); + difference = true; + } + } + if (difference) { + minY = qMin(minY, y); + maxY = qMax(maxY, y); + } + } + QByteArray delta; + int deltaWidth = 0, deltaHeight = 0; + if (maxX >= minX) { + deltaWidth = maxX - minX + 1; + deltaHeight = maxY - minY + 1; + delta = QByteArray(deltaWidth * deltaHeight * DataBlock::COLOR_BYTES, 0); + char* dest = delta.data(); + src = _contents.constData() + (minY * _width + minX) * DataBlock::COLOR_BYTES; + for (int y = 0; y < deltaHeight; y++, src += _width * DataBlock::COLOR_BYTES, + dest += deltaWidth * DataBlock::COLOR_BYTES) { + memcpy(dest, src, deltaWidth * DataBlock::COLOR_BYTES); + } + } + reference->setEncodedDelta(encodeHeightfieldColor(minX + 1, minY + 1, deltaWidth, deltaHeight, delta)); + reference->setDeltaData(DataBlockPointer(this)); + } + out << reference->getEncodedDelta().size(); + out.writeAligned(reference->getEncodedDelta()); +} + +void HeightfieldColor::read(Bitstream& in, int bytes) { + int offsetX, offsetY, height; + _contents = decodeHeightfieldColor(_encoded = in.readAligned(bytes), offsetX, offsetY, _width, height); +} + +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(in, size); + } + return in; +} + +template<> void Bitstream::writeRawDelta(const HeightfieldColorPointer& value, const HeightfieldColorPointer& reference) { + if (value) { + value->writeDelta(*this, reference); + } else { + *this << 0; + } +} + +template<> void Bitstream::readRawDelta(HeightfieldColorPointer& value, const HeightfieldColorPointer& reference) { + int size; + *this >> size; + if (size == 0) { + value = HeightfieldColorPointer(); + } else { + value = new HeightfieldColor(*this, size, reference); + } +} + +HeightfieldColorEditor::HeightfieldColorEditor(QWidget* parent) : + QWidget(parent) { + + QHBoxLayout* layout = new QHBoxLayout(); + setLayout(layout); + + layout->addWidget(_select = new QPushButton("Select")); + connect(_select, &QPushButton::clicked, this, &HeightfieldColorEditor::select); + + layout->addWidget(_clear = new QPushButton("Clear")); + connect(_clear, &QPushButton::clicked, this, &HeightfieldColorEditor::clear); + _clear->setEnabled(false); +} + +void HeightfieldColorEditor::setColor(const HeightfieldColorPointer& color) { + if ((_color = color)) { + _clear->setEnabled(true); + } else { + _clear->setEnabled(false); + } +} + +void HeightfieldColorEditor::select() { + QSettings settings; + QString result = QFileDialog::getOpenFileName(this, "Select Color Image", settings.value("heightDir").toString(), + "Images (*.png *.jpg *.bmp)"); + if (result.isNull()) { + return; + } + settings.setValue("heightDir", QFileInfo(result).path()); + QImage image; + if (!image.load(result)) { + QMessageBox::warning(this, "Invalid Image", "The selected image could not be read."); + return; + } + image = image.convertToFormat(QImage::Format_RGB888); + int width = getHeightfieldSize(image.width()); + int height = getHeightfieldSize(image.height()); + QByteArray contents(width * height * DataBlock::COLOR_BYTES, 0); + char* dest = contents.data(); + for (int i = 0; i < image.height(); i++, dest += width * DataBlock::COLOR_BYTES) { + memcpy(dest, image.constScanLine(i), image.width() * DataBlock::COLOR_BYTES); + } + emit colorChanged(_color = new HeightfieldColor(width, contents)); + _clear->setEnabled(true); +} + +void HeightfieldColorEditor::clear() { + emit colorChanged(_color = HeightfieldColorPointer()); + _clear->setEnabled(false); +} + +static QByteArray encodeHeightfieldMaterial(int offsetX, int offsetY, int width, int height, const QByteArray& contents) { + QByteArray inflated(HEIGHTFIELD_DATA_HEADER_SIZE, 0); + qint32* header = (qint32*)inflated.data(); + *header++ = offsetX; + *header++ = offsetY; + *header++ = width; + *header++ = height; + inflated.append(contents); + return qCompress(inflated); +} + +static QByteArray decodeHeightfieldMaterial(const QByteArray& encoded, int& offsetX, int& offsetY, int& width, int& height) { + QByteArray inflated = qUncompress(encoded); + const qint32* header = (const qint32*)inflated.constData(); + offsetX = *header++; + offsetY = *header++; + width = *header++; + height = *header++; + return inflated.mid(HEIGHTFIELD_DATA_HEADER_SIZE); +} + +HeightfieldMaterial::HeightfieldMaterial(int width, const QByteArray& contents, + const QVector& materials) : + HeightfieldData(width), + _contents(contents), + _materials(materials) { +} + +HeightfieldMaterial::HeightfieldMaterial(Bitstream& in, int bytes) { + read(in, bytes); +} + +HeightfieldMaterial::HeightfieldMaterial(Bitstream& in, int bytes, const HeightfieldMaterialPointer& reference) { + if (!reference) { + read(in, bytes); + return; + } + QMutexLocker locker(&reference->getEncodedDeltaMutex()); + reference->setEncodedDelta(in.readAligned(bytes)); + in.readDelta(_materials, reference->getMaterials()); + reference->setDeltaData(DataBlockPointer(this)); + _width = reference->getWidth(); + _contents = reference->getContents(); + + int offsetX, offsetY, width, height; + QByteArray delta = decodeHeightfieldMaterial(reference->getEncodedDelta(), offsetX, offsetY, width, height); + if (delta.isEmpty()) { + return; + } + if (offsetX == 0) { + _contents = delta; + _width = width; + return; + } + int minX = offsetX - 1; + int minY = offsetY - 1; + const char* src = delta.constData(); + char* dest = _contents.data() + minY * _width + minX; + for (int y = 0; y < height; y++, src += width, dest += _width) { + memcpy(dest, src, width); + } +} + +void HeightfieldMaterial::write(Bitstream& out) { + QMutexLocker locker(&_encodedMutex); + if (_encoded.isEmpty()) { + _encoded = encodeHeightfieldMaterial(0, 0, _width, _contents.size() / _width, _contents); + } + out << _encoded.size(); + out.writeAligned(_encoded); + out << _materials; +} + +void HeightfieldMaterial::writeDelta(Bitstream& out, const HeightfieldMaterialPointer& reference) { + if (!reference) { + write(out); + return; + } + QMutexLocker locker(&reference->getEncodedDeltaMutex()); + if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { + if (reference->getWidth() != _width || reference->getContents().size() != _contents.size()) { + reference->setEncodedDelta(encodeHeightfieldMaterial(0, 0, _width, _contents.size() / _width, _contents)); + + } else { + int height = _contents.size() / _width; + int minX = _width, minY = height; + int maxX = -1, maxY = -1; + const char* src = _contents.constData(); + const char* ref = reference->getContents().constData(); + for (int y = 0; y < height; y++) { + bool difference = false; + for (int x = 0; x < _width; x++) { + if (*src++ != *ref++) { + minX = qMin(minX, x); + maxX = qMax(maxX, x); + difference = true; + } + } + if (difference) { + minY = qMin(minY, y); + maxY = qMax(maxY, y); + } + } + QByteArray delta; + int deltaWidth = 0, deltaHeight = 0; + if (maxX >= minX) { + deltaWidth = maxX - minX + 1; + deltaHeight = maxY - minY + 1; + delta = QByteArray(deltaWidth * deltaHeight, 0); + char* dest = delta.data(); + src = _contents.constData() + minY * _width + minX; + for (int y = 0; y < deltaHeight; y++, src += _width, dest += deltaWidth) { + memcpy(dest, src, deltaWidth); + } + } + reference->setEncodedDelta(encodeHeightfieldMaterial(minX + 1, minY + 1, deltaWidth, deltaHeight, delta)); + } + reference->setDeltaData(DataBlockPointer(this)); + } + out << reference->getEncodedDelta().size(); + out.writeAligned(reference->getEncodedDelta()); + out.writeDelta(_materials, reference->getMaterials()); +} + +void HeightfieldMaterial::read(Bitstream& in, int bytes) { + int offsetX, offsetY, height; + _contents = decodeHeightfieldMaterial(_encoded = in.readAligned(bytes), offsetX, offsetY, _width, height); + in >> _materials; +} + +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(in, size); + } + return in; +} + +template<> void Bitstream::writeRawDelta(const HeightfieldMaterialPointer& value, + const HeightfieldMaterialPointer& reference) { + if (value) { + value->writeDelta(*this, reference); + } else { + *this << 0; + } +} + +template<> void Bitstream::readRawDelta(HeightfieldMaterialPointer& value, const HeightfieldMaterialPointer& reference) { + int size; + *this >> size; + if (size == 0) { + value = HeightfieldMaterialPointer(); + } else { + value = new HeightfieldMaterial(*this, size, 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); + } +} + +bool Heightfield::isHeightfield() const { + return true; +} + +float Heightfield::getHeight(const glm::vec3& location) const { + if (!_height) { + return -FLT_MAX; + } + int width = _height->getWidth(); + const QVector& contents = _height->getContents(); + const quint16* src = contents.constData(); + int height = contents.size() / width; + int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION; + int innerHeight = height - HeightfieldHeight::HEIGHT_EXTENSION; + + glm::vec3 relative = glm::inverse(getRotation()) * (location - getTranslation()) * glm::vec3(1.0f / getScale(), + 1.0f, 1.0f / (getScale() * _aspectZ)); + relative.x = relative.x * innerWidth + HeightfieldHeight::HEIGHT_BORDER; + relative.z = relative.z * innerHeight + HeightfieldHeight::HEIGHT_BORDER; + if (relative.x < 0.0f || relative.z < 0.0f || relative.x > width - 1 || relative.z > height - 1) { + return -FLT_MAX; + } + + // find the bounds of the cell containing the point and the shared vertex heights + 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; + 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); + } + if (interpolatedHeight == 0.0f) { + return -FLT_MAX; // ignore zero values + } + + // convert the interpolated height into world space + return getTranslation().y + interpolatedHeight * getScale() * _aspectY / numeric_limits::max(); +} + +bool Heightfield::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { + if (!_height) { + return false; + } + int width = _height->getWidth(); + const QVector& contents = _height->getContents(); + const quint16* src = contents.constData(); + int height = contents.size() / width; + int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION; + int innerHeight = height - HeightfieldHeight::HEIGHT_EXTENSION; + int highestX = innerWidth + HeightfieldHeight::HEIGHT_BORDER; + int highestZ = innerHeight + HeightfieldHeight::HEIGHT_BORDER; + + glm::quat inverseRotation = glm::inverse(getRotation()); + glm::vec3 inverseScale(innerWidth / getScale(), numeric_limits::max() / (getScale() * _aspectY), + innerHeight / (getScale() * _aspectZ)); + glm::vec3 dir = inverseRotation * direction * inverseScale; + glm::vec3 entry = inverseRotation * (origin - getTranslation()) * inverseScale; + + float boundsDistance; + if (!Box(glm::vec3(), glm::vec3((float)innerWidth, (float)numeric_limits::max(), + (float)innerHeight)).findRayIntersection(entry, dir, boundsDistance)) { + return false; + } + entry += dir * boundsDistance; + + entry.x += HeightfieldHeight::HEIGHT_BORDER; + entry.z += HeightfieldHeight::HEIGHT_BORDER; + glm::vec3 floors = glm::floor(entry); + glm::vec3 ceils = glm::ceil(entry); + if (floors.x == ceils.x) { + if (dir.x > 0.0f) { + ceils.x += 1.0f; + } else { + floors.x -= 1.0f; + } + } + if (floors.z == ceils.z) { + if (dir.z > 0.0f) { + ceils.z += 1.0f; + } else { + floors.z -= 1.0f; + } + } + + bool withinBounds = true; + float accumulatedDistance = 0.0f; + while (withinBounds) { + // find the heights at the corners of the current cell + int floorX = qMin(qMax((int)floors.x, HeightfieldHeight::HEIGHT_BORDER), highestX); + int floorZ = qMin(qMax((int)floors.z, HeightfieldHeight::HEIGHT_BORDER), highestZ); + int ceilX = qMin(qMax((int)ceils.x, HeightfieldHeight::HEIGHT_BORDER), highestX); + int ceilZ = qMin(qMax((int)ceils.z, HeightfieldHeight::HEIGHT_BORDER), highestZ); + float upperLeft = src[floorZ * width + floorX]; + float upperRight = src[floorZ * width + ceilX]; + float lowerLeft = src[ceilZ * width + floorX]; + float lowerRight = src[ceilZ * width + ceilX]; + + // find the distance to the next x coordinate + float xDistance = FLT_MAX; + if (dir.x > 0.0f) { + xDistance = (ceils.x - entry.x) / dir.x; + } else if (dir.x < 0.0f) { + xDistance = (floors.x - entry.x) / dir.x; + } + + // and the distance to the next z coordinate + float zDistance = FLT_MAX; + if (dir.z > 0.0f) { + zDistance = (ceils.z - entry.z) / dir.z; + } else if (dir.z < 0.0f) { + zDistance = (floors.z - entry.z) / dir.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) { + if (dir.y > 0.0f) { + return false; // line points upwards; no collisions possible + } + withinBounds = false; // line points 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 * dir; + withinBounds = (exit.y >= 0.0f && exit.y <= numeric_limits::max()); + if (exitDistance == xDistance) { + if (dir.x > 0.0f) { + nextFloors.x += 1.0f; + withinBounds &= (nextCeils.x += 1.0f) <= highestX; + } else { + withinBounds &= (nextFloors.x -= 1.0f) >= HeightfieldHeight::HEIGHT_BORDER; + nextCeils.x -= 1.0f; + } + } + if (exitDistance == zDistance) { + if (dir.z > 0.0f) { + nextFloors.z += 1.0f; + withinBounds &= (nextCeils.z += 1.0f) <= highestZ; + } else { + withinBounds &= (nextFloors.z -= 1.0f) >= HeightfieldHeight::HEIGHT_BORDER; + 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, dir); + if (lowerProduct < 0.0f) { + float planeDistance = -glm::dot(lowerNormal, relativeEntry) / lowerProduct; + glm::vec3 intersection = relativeEntry + planeDistance * dir; + if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && + intersection.z >= intersection.x) { + distance = boundsDistance + accumulatedDistance + planeDistance; + return true; + } + } + + // then the one with the X+ segment + glm::vec3 upperNormal(upperLeft - upperRight, 1.0f, upperRight - lowerRight); + float upperProduct = glm::dot(upperNormal, dir); + if (upperProduct < 0.0f) { + float planeDistance = -glm::dot(upperNormal, relativeEntry) / upperProduct; + glm::vec3 intersection = relativeEntry + planeDistance * dir; + if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && + intersection.x >= intersection.z) { + distance = boundsDistance + accumulatedDistance + planeDistance; + return true; + } + } + + // no joy; continue on our way + entry = exit; + floors = nextFloors; + ceils = nextCeils; + accumulatedDistance += exitDistance; + } + + return false; +} + +Spanner* Heightfield::paintMaterial(const glm::vec3& position, float radius, + const SharedObjectPointer& material, const QColor& color) { + if (!_height) { + return this; + } + int heightWidth = _height->getWidth(); + int heightHeight = _height->getContents().size() / heightWidth; + int baseWidth = heightWidth - HeightfieldHeight::HEIGHT_EXTENSION + HeightfieldData::SHARED_EDGE; + int baseHeight = heightHeight - HeightfieldHeight::HEIGHT_EXTENSION + HeightfieldData::SHARED_EDGE; + Heightfield* newHeightfield = static_cast(clone(true)); + + int colorWidth = baseWidth, colorHeight = baseHeight; + QByteArray colorContents; + if (_color) { + colorWidth = _color->getWidth(); + colorHeight = _color->getContents().size() / (colorWidth * DataBlock::COLOR_BYTES); + colorContents = _color->getContents(); + + } else { + colorContents = QByteArray(baseWidth * baseHeight * DataBlock::COLOR_BYTES, 0xFF); + } + + int materialWidth = baseWidth, materialHeight = baseHeight; + QByteArray materialContents; + QVector materials; + if (_material) { + materialWidth = _material->getWidth(); + materialHeight = _material->getContents().size() / materialWidth; + materialContents = _material->getContents(); + materials = _material->getMaterials(); + + } else { + materialContents = QByteArray(baseWidth * baseHeight, 0); + } + + int highestX = colorWidth - 1; + int highestZ = colorHeight - 1; + glm::vec3 inverseScale(highestX / getScale(), 1.0f, highestZ / (getScale() * _aspectZ)); + glm::vec3 center = glm::inverse(getRotation()) * (position - getTranslation()) * inverseScale; + + glm::vec3 extents = glm::vec3(radius, radius, radius) * inverseScale; + glm::vec3 start = glm::floor(center - extents); + glm::vec3 end = glm::ceil(center + extents); + + // paint all points within the radius + float z = qMax(start.z, 0.0f); + float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highestX); + int stride = colorWidth * DataBlock::COLOR_BYTES; + uchar* lineDest = (uchar*)colorContents.data() + (int)z * stride + (int)startX * DataBlock::COLOR_BYTES; + float squaredRadius = extents.x * extents.x; + float multiplierZ = inverseScale.x / inverseScale.z; + char red = color.red(), green = color.green(), blue = color.blue(); + bool changed = false; + for (float endZ = qMin(end.z, (float)highestZ); z <= endZ; z += 1.0f) { + uchar* dest = lineDest; + for (float x = startX; x <= endX; x += 1.0f, dest += DataBlock::COLOR_BYTES) { + float dx = x - center.x, dz = (z - center.z) * multiplierZ; + if (dx * dx + dz * dz <= squaredRadius) { + dest[0] = red; + dest[1] = green; + dest[2] = blue; + changed = true; + } + } + lineDest += stride; + } + if (changed) { + newHeightfield->setColor(HeightfieldColorPointer(new HeightfieldColor(colorWidth, colorContents))); + } + + highestX = materialWidth - 1; + highestZ = materialHeight - 1; + inverseScale = glm::vec3(highestX / getScale(), 1.0f, highestZ / (getScale() * _aspectZ)); + center = glm::inverse(getRotation()) * (position - getTranslation()) * inverseScale; + + extents = glm::vec3(radius, radius, radius) * inverseScale; + start = glm::floor(center - extents); + end = glm::ceil(center + extents); + + // paint all points within the radius + z = qMax(start.z, 0.0f); + startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highestX); + lineDest = (uchar*)materialContents.data() + (int)z * materialWidth + (int)startX; + squaredRadius = extents.x * extents.x; + uchar materialIndex = getMaterialIndex(material, materials, materialContents); + changed = false; + for (float endZ = qMin(end.z, (float)highestZ); z <= endZ; z += 1.0f) { + uchar* dest = lineDest; + for (float x = startX; x <= endX; x += 1.0f, dest++) { + float dx = x - center.x, dz = (z - center.z) * multiplierZ; + if (dx * dx + dz * dz <= squaredRadius) { + *dest = materialIndex; + changed = true; + } + } + lineDest += materialWidth; + } + if (changed) { + clearUnusedMaterials(materials, materialContents); + newHeightfield->setMaterial(HeightfieldMaterialPointer(new HeightfieldMaterial(materialWidth, + materialContents, materials))); + } + + return newHeightfield; +} + +Spanner* Heightfield::paintHeight(const glm::vec3& position, float radius, float height) { + if (!_height) { + return this; + } + int heightWidth = _height->getWidth(); + int heightHeight = _height->getContents().size() / heightWidth; + QVector contents = _height->getContents(); + int innerWidth = heightWidth - HeightfieldHeight::HEIGHT_EXTENSION; + int innerHeight = heightHeight - HeightfieldHeight::HEIGHT_EXTENSION; + int highestX = heightWidth - 1; + int highestZ = heightHeight - 1; + Heightfield* newHeightfield = static_cast(clone(true)); + + glm::vec3 inverseScale(innerWidth / getScale(), 1.0f, innerHeight / (getScale() * _aspectZ)); + glm::vec3 center = glm::inverse(getRotation()) * (position - getTranslation()) * inverseScale; + center.x += 1.0f; + center.z += 1.0f; + + glm::vec3 extents = glm::vec3(radius, radius, radius) * inverseScale; + glm::vec3 start = glm::floor(center - extents); + glm::vec3 end = glm::ceil(center + extents); + + // first see if we're going to exceed the range limits + float z = qMax(start.z, 0.0f); + float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highestX); + quint16* lineDest = contents.data() + (int)z * heightWidth + (int)startX; + float squaredRadius = extents.x * extents.x; + float squaredRadiusReciprocal = 1.0f / squaredRadius; + float scaledHeight = height * numeric_limits::max() / (getScale() * _aspectY); + float multiplierZ = inverseScale.x / inverseScale.z; + int minimumValue = 1, maximumValue = numeric_limits::max(); + for (float endZ = qMin(end.z, (float)highestZ); z <= endZ; z += 1.0f) { + quint16* dest = lineDest; + for (float x = startX; x <= endX; x += 1.0f, dest++) { + float dx = x - center.x, dz = (z - center.z) * multiplierZ; + float distanceSquared = dx * dx + dz * dz; + if (distanceSquared <= squaredRadius) { + // height falls off towards edges + int value = *dest; + if (value != 0) { + value += scaledHeight * (squaredRadius - distanceSquared) * squaredRadiusReciprocal; + minimumValue = qMin(minimumValue, value); + maximumValue = qMax(maximumValue, value); + } + } + } + lineDest += heightWidth; + } + + // renormalize if necessary + if (minimumValue < 1 || maximumValue > numeric_limits::max()) { + float scale = (numeric_limits::max() - 1.0f) / (maximumValue - minimumValue); + float offset = 1.0f - minimumValue; + newHeightfield->setAspectY(_aspectY / scale); + newHeightfield->setTranslation(getTranslation() - getRotation() * + glm::vec3(0.0f, offset * _aspectY * getScale() / (numeric_limits::max() - 1), 0.0f)); + for (quint16* dest = contents.data(), *end = contents.data() + contents.size(); dest != end; dest++) { + int value = *dest; + if (value != 0) { + *dest = (value + offset) * scale; + } + } + } + + // now apply the actual change + z = qMax(start.z, 0.0f); + lineDest = contents.data() + (int)z * heightWidth + (int)startX; + scaledHeight = height * numeric_limits::max() / (getScale() * newHeightfield->getAspectY()); + bool changed = false; + for (float endZ = qMin(end.z, (float)highestZ); z <= endZ; z += 1.0f) { + quint16* dest = lineDest; + for (float x = startX; x <= endX; x += 1.0f, dest++) { + float dx = x - center.x, dz = (z - center.z) * multiplierZ; + float distanceSquared = dx * dx + dz * dz; + if (distanceSquared <= squaredRadius) { + // height falls off towards edges + int value = *dest; + if (value != 0) { + *dest = value + scaledHeight * (squaredRadius - distanceSquared) * squaredRadiusReciprocal; + changed = true; + } + } + } + lineDest += heightWidth; + } + if (changed) { + newHeightfield->setHeight(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, contents))); + } + + return newHeightfield; +} + +Spanner* Heightfield::clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield) { + if (!_height) { + return this; + } + int heightWidth = _height->getWidth(); + int heightHeight = _height->getContents().size() / heightWidth; + int innerHeightWidth = heightWidth - HeightfieldHeight::HEIGHT_EXTENSION; + int innerHeightHeight = heightHeight - HeightfieldHeight::HEIGHT_EXTENSION; + float heightIncrementX = getScale() / innerHeightWidth; + float heightIncrementZ = (getScale() * _aspectZ) / innerHeightHeight; + + int colorWidth = heightWidth; + int colorHeight = heightHeight; + if (_color) { + colorWidth = _color->getWidth(); + colorHeight = _color->getContents().size() / (colorWidth * DataBlock::COLOR_BYTES); + } + int innerColorWidth = colorWidth - HeightfieldData::SHARED_EDGE; + int innerColorHeight = colorHeight - HeightfieldData::SHARED_EDGE; + float colorIncrementX = getScale() / innerColorWidth; + float colorIncrementZ = (getScale() * _aspectZ) / innerColorHeight; + + int materialWidth = colorWidth; + int materialHeight = colorHeight; + if (_material) { + materialWidth = _material->getWidth(); + materialHeight = _material->getContents().size() / materialWidth; + } + int innerMaterialWidth = materialWidth - HeightfieldData::SHARED_EDGE; + int innerMaterialHeight = materialHeight - HeightfieldData::SHARED_EDGE; + float materialIncrementX = getScale() / innerMaterialWidth; + float materialIncrementZ = (getScale() * _aspectZ) / innerMaterialHeight; + + float largestIncrementX = qMax(heightIncrementX, qMax(colorIncrementX, materialIncrementX)); + float largestIncrementZ = qMax(heightIncrementZ, qMax(colorIncrementZ, materialIncrementZ)); + + glm::vec3 minimum(glm::floor(bounds.minimum.x / largestIncrementX) * largestIncrementX, getBounds().minimum.y, + glm::floor(bounds.minimum.z / largestIncrementZ) * largestIncrementZ); + glm::vec3 maximum(glm::ceil(bounds.maximum.x / largestIncrementX) * largestIncrementX, getBounds().maximum.y, + glm::ceil(bounds.maximum.z / largestIncrementZ) * largestIncrementZ); + Box largestBounds(minimum, maximum); + + // enlarge the area to fetch + minimum.x -= largestIncrementX; + maximum.x += largestIncrementX; + minimum.z -= largestIncrementZ; + maximum.z += largestIncrementX; + + glm::mat4 baseTransform = glm::mat4_cast(glm::inverse(getRotation())) * glm::translate(-getTranslation()); + glm::vec3 inverseScale(innerHeightWidth / getScale(), 1.0f, innerHeightHeight / (getScale() * _aspectZ)); + glm::mat4 transform = glm::scale(inverseScale) * baseTransform; + Box transformedBounds = transform * largestBounds; + + // make sure there are values to clear + int startX = glm::clamp((int)glm::ceil(transformedBounds.minimum.x) + HeightfieldHeight::HEIGHT_BORDER, + 0, heightWidth - 1); + int startZ = glm::clamp((int)glm::ceil(transformedBounds.minimum.z) + HeightfieldHeight::HEIGHT_BORDER, + 0, heightHeight - 1); + int endX = glm::clamp((int)glm::floor(transformedBounds.maximum.x) + HeightfieldHeight::HEIGHT_BORDER, 0, heightWidth - 1); + int endZ = glm::clamp((int)glm::floor(transformedBounds.maximum.z) + HeightfieldHeight::HEIGHT_BORDER, + 0, heightHeight - 1); + const quint16* src = _height->getContents().constData() + startZ * heightWidth + startX; + for (int z = startZ; z <= endZ; z++, src += heightWidth) { + const quint16* lineSrc = src; + for (int x = startX; x <= endX; x++) { + if (*lineSrc++ != 0) { + goto clearableBreak; + } + } + } + return this; + clearableBreak: + + int spannerHeightWidth = (int)((maximum.x - minimum.x) / heightIncrementX) + HeightfieldHeight::HEIGHT_EXTENSION; + int spannerHeightHeight = (int)((maximum.z - minimum.z) / heightIncrementZ) + HeightfieldHeight::HEIGHT_EXTENSION; + int spannerColorWidth = (int)((maximum.x - minimum.x) / colorIncrementX) + HeightfieldData::SHARED_EDGE; + int spannerColorHeight = (int)((maximum.z - minimum.z) / colorIncrementZ) + HeightfieldData::SHARED_EDGE; + int spannerMaterialWidth = (int)((maximum.x - minimum.x) / materialIncrementX) + HeightfieldData::SHARED_EDGE; + int spannerMaterialHeight = (int)((maximum.z - minimum.z) / materialIncrementZ) + HeightfieldData::SHARED_EDGE; + + // create heightfield if necessary + Heightfield* spanner = static_cast(heightfield.data()); + if (!spanner) { + heightfield = spanner = new Heightfield(); + spanner->setTranslation(minimum); + spanner->setScale(maximum.x - minimum.x); + spanner->setAspectY((maximum.y - minimum.y) / spanner->getScale()); + spanner->setAspectZ((maximum.z - minimum.z) / spanner->getScale()); + spanner->setHeight(HeightfieldHeightPointer(new HeightfieldHeight(spannerHeightWidth, + QVector(spannerHeightWidth * spannerHeightHeight)))); + spanner->setColor(HeightfieldColorPointer(new HeightfieldColor(spannerColorWidth, + QByteArray(spannerColorWidth * spannerColorHeight * DataBlock::COLOR_BYTES, 0xFF)))); + spanner->setMaterial(HeightfieldMaterialPointer(new HeightfieldMaterial(spannerMaterialWidth, + QByteArray(spannerMaterialWidth * spannerMaterialHeight, 0), QVector()))); + } + + // fetch the height + glm::vec3 spannerInverseScale((spannerHeightWidth - HeightfieldHeight::HEIGHT_EXTENSION) / spanner->getScale(), 1.0f, + (spannerHeightHeight - HeightfieldHeight::HEIGHT_EXTENSION) / (spanner->getScale() * spanner->getAspectZ())); + glm::mat4 spannerBaseTransform = glm::translate(-spanner->getTranslation()); + glm::mat4 spannerTransform = glm::scale(spannerInverseScale) * spannerBaseTransform; + Box spannerTransformedBounds = spannerTransform * getBounds(); + int spannerStartX = glm::clamp((int)glm::floor(spannerTransformedBounds.minimum.x) + HeightfieldHeight::HEIGHT_BORDER, + 0, spannerHeightWidth - 1); + int spannerStartZ = glm::clamp((int)glm::floor(spannerTransformedBounds.minimum.z) + HeightfieldHeight::HEIGHT_BORDER, + 0, spannerHeightHeight - 1); + int spannerEndX = glm::clamp((int)glm::ceil(spannerTransformedBounds.maximum.x) + HeightfieldHeight::HEIGHT_BORDER, + 0, spannerHeightWidth - 1); + int spannerEndZ = glm::clamp((int)glm::ceil(spannerTransformedBounds.maximum.z) + HeightfieldHeight::HEIGHT_BORDER, + 0, spannerHeightHeight - 1); + quint16* dest = spanner->getHeight()->getContents().data() + spannerStartZ * spannerHeightWidth + spannerStartX; + glm::vec3 step = 1.0f / spannerInverseScale; + glm::vec3 initialPosition = glm::vec3(spannerStartX - HeightfieldHeight::HEIGHT_BORDER, 0, + spannerStartZ - HeightfieldHeight::HEIGHT_BORDER) * step + spanner->getTranslation(); + glm::vec3 position = initialPosition; + float heightScale = numeric_limits::max() / (getScale() * _aspectY); + for (int z = spannerStartZ; z <= spannerEndZ; z++, dest += spannerHeightWidth, position.z += step.z) { + quint16* lineDest = dest; + position.x = initialPosition.x; + for (int x = spannerStartX; x <= spannerEndX; x++, lineDest++, position.x += step.x) { + float height = (getHeight(position) - getTranslation().y) * heightScale; + if (height > *lineDest) { + *lineDest = height; + } + } + } + + // and the color + if (_color) { + spannerInverseScale = glm::vec3((spannerColorWidth - HeightfieldData::SHARED_EDGE) / spanner->getScale(), 1.0f, + (spannerColorHeight - HeightfieldData::SHARED_EDGE) / (spanner->getScale() * spanner->getAspectZ())); + spannerTransform = glm::scale(spannerInverseScale) * spannerBaseTransform; + spannerTransformedBounds = spannerTransform * getBounds(); + spannerStartX = glm::clamp((int)glm::floor(spannerTransformedBounds.minimum.x), 0, spannerColorWidth - 1); + spannerStartZ = glm::clamp((int)glm::floor(spannerTransformedBounds.minimum.z), 0, spannerColorHeight - 1); + spannerEndX = glm::clamp((int)glm::ceil(spannerTransformedBounds.maximum.x), 0, spannerColorWidth - 1); + spannerEndZ = glm::clamp((int)glm::ceil(spannerTransformedBounds.maximum.z), 0, spannerColorHeight - 1); + + char* dest = spanner->getColor()->getContents().data() + + (spannerStartZ * spannerColorWidth + spannerStartX) * DataBlock::COLOR_BYTES; + step = 1.0f / spannerInverseScale; + initialPosition = glm::vec3(spannerStartX, 0, spannerStartZ) * step + spanner->getTranslation(); + position = initialPosition; + for (int z = spannerStartZ; z <= spannerEndZ; z++, dest += spannerColorWidth * DataBlock::COLOR_BYTES, + position.z += step.z) { + char* lineDest = dest; + position.x = initialPosition.x; + for (int x = spannerStartX; x <= spannerEndX; x++, lineDest += DataBlock::COLOR_BYTES, position.x += step.x) { + QRgb color = getColorAt(position); + if (color != 0) { + lineDest[0] = qRed(color); + lineDest[1] = qGreen(color); + lineDest[2] = qBlue(color); + } + } + } + } + + // and the material + if (_material) { + spannerInverseScale = glm::vec3((spannerMaterialWidth - HeightfieldData::SHARED_EDGE) / spanner->getScale(), 1.0f, + (spannerMaterialHeight - HeightfieldData::SHARED_EDGE) / (spanner->getScale() * spanner->getAspectZ())); + spannerTransform = glm::scale(spannerInverseScale) * spannerBaseTransform; + spannerTransformedBounds = spannerTransform * getBounds(); + spannerStartX = glm::clamp((int)glm::floor(spannerTransformedBounds.minimum.x), 0, spannerMaterialWidth - 1); + spannerStartZ = glm::clamp((int)glm::floor(spannerTransformedBounds.minimum.z), 0, spannerMaterialHeight - 1); + spannerEndX = glm::clamp((int)glm::ceil(spannerTransformedBounds.maximum.x), 0, spannerMaterialWidth - 1); + spannerEndZ = glm::clamp((int)glm::ceil(spannerTransformedBounds.maximum.z), 0, spannerMaterialHeight - 1); + + char* dest = spanner->getMaterial()->getContents().data() + spannerStartZ * spannerMaterialWidth + spannerStartX; + step = 1.0f / spannerInverseScale; + initialPosition = glm::vec3(spannerStartX, 0, spannerStartZ) * step + spanner->getTranslation(); + position = initialPosition; + QHash materialMap; + for (int z = spannerStartZ; z <= spannerEndZ; z++, dest += spannerMaterialWidth, position.z += step.z) { + char* lineDest = dest; + position.x = initialPosition.x; + for (int x = spannerStartX; x <= spannerEndX; x++, lineDest++, position.x += step.x) { + int material = getMaterialAt(position); + if (material != -1) { + if (material != 0) { + int& mapping = materialMap[material]; + if (mapping == 0) { + material = mapping = getMaterialIndex(_material->getMaterials().at(material - 1), + spanner->getMaterial()->getMaterials(), spanner->getMaterial()->getContents()); + } + } + *lineDest = material; + } + } + } + } + + // clear the height + QVector newHeightContents = _height->getContents(); + dest = newHeightContents.data() + startZ * heightWidth + startX; + for (int z = startZ; z <= endZ; z++, dest += heightWidth) { + memset(dest, 0, (endX - startX + 1) * sizeof(quint16)); + } + + // if we've cleared all the inner height, we can remove the spanner entirely + src = newHeightContents.constData() + heightWidth + HeightfieldHeight::HEIGHT_BORDER; + for (int z = 0; z < innerHeightHeight; z++, src += heightWidth) { + const quint16* lineSrc = src; + for (int x = 0; x < innerHeightWidth; x++) { + if (*lineSrc++ != 0) { + goto nonEmptyBreak; + } + } + } + return NULL; + nonEmptyBreak: + + Heightfield* newHeightfield = static_cast(clone(true)); + newHeightfield->setHeight(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, newHeightContents))); + + // and the color + if (_color) { + inverseScale = glm::vec3(innerColorWidth / getScale(), 1.0f, innerColorHeight / (getScale() * _aspectZ)); + transform = glm::scale(inverseScale) * baseTransform; + transformedBounds = transform * largestBounds; + startX = glm::clamp((int)glm::ceil(transformedBounds.minimum.x), 0, colorWidth - 1); + startZ = glm::clamp((int)glm::ceil(transformedBounds.minimum.z), 0, colorHeight - 1); + endX = glm::clamp((int)glm::floor(transformedBounds.maximum.x), 0, colorWidth - 1); + endZ = glm::clamp((int)glm::floor(transformedBounds.maximum.z), 0, colorHeight - 1); + QByteArray newColorContents = _color->getContents(); + char* dest = newColorContents.data() + (startZ * colorWidth + startX) * DataBlock::COLOR_BYTES; + for (int z = startZ; z <= endZ; z++, dest += colorWidth * DataBlock::COLOR_BYTES) { + memset(dest, 0, (endX - startX + 1) * DataBlock::COLOR_BYTES); + } + newHeightfield->setColor(HeightfieldColorPointer(new HeightfieldColor(colorWidth, newColorContents))); + } + + // and the material + if (_material) { + inverseScale = glm::vec3(innerMaterialWidth / getScale(), 1.0f, innerMaterialHeight / (getScale() * _aspectZ)); + transform = glm::scale(inverseScale) * baseTransform; + transformedBounds = transform * largestBounds; + startX = glm::clamp((int)glm::ceil(transformedBounds.minimum.x), 0, materialWidth - 1); + startZ = glm::clamp((int)glm::ceil(transformedBounds.minimum.z), 0, materialHeight - 1); + endX = glm::clamp((int)glm::floor(transformedBounds.maximum.x), 0, materialWidth - 1); + endZ = glm::clamp((int)glm::floor(transformedBounds.maximum.z), 0, materialHeight - 1); + QByteArray newMaterialContents = _material->getContents(); + QVector newMaterials = _material->getMaterials(); + char* dest = newMaterialContents.data() + startZ * materialWidth + startX; + for (int z = startZ; z <= endZ; z++, dest += materialWidth) { + memset(dest, 0, endX - startX + 1); + } + clearUnusedMaterials(newMaterials, newMaterialContents); + newHeightfield->setMaterial(HeightfieldMaterialPointer(new HeightfieldMaterial( + materialWidth, newMaterialContents, newMaterials))); + } + + return newHeightfield; +} + +bool Heightfield::hasOwnColors() const { + return _color; +} + +bool Heightfield::hasOwnMaterials() const { + return _material; +} + +QRgb Heightfield::getColorAt(const glm::vec3& point) { + int width = _color->getWidth(); + const QByteArray& contents = _color->getContents(); + const uchar* src = (const uchar*)contents.constData(); + int height = contents.size() / (width * DataBlock::COLOR_BYTES); + int innerWidth = width - HeightfieldData::SHARED_EDGE; + int innerHeight = height - HeightfieldData::SHARED_EDGE; + + glm::vec3 relative = glm::inverse(getRotation()) * (point - getTranslation()) * glm::vec3(innerWidth / getScale(), + 1.0f, innerHeight / (getScale() * _aspectZ)); + if (relative.x < 0.0f || relative.z < 0.0f || relative.x > width - 1 || relative.z > height - 1) { + return 0; + } + 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* 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) { + int width = _material->getWidth(); + const QByteArray& contents = _material->getContents(); + const uchar* src = (const uchar*)contents.constData(); + int height = contents.size() / width; + int innerWidth = width - HeightfieldData::SHARED_EDGE; + int innerHeight = height - HeightfieldData::SHARED_EDGE; + + glm::vec3 relative = glm::inverse(getRotation()) * (point - getTranslation()) * glm::vec3(innerWidth / getScale(), + 1.0f, innerHeight / (getScale() * _aspectZ)); + if (relative.x < 0.0f || relative.z < 0.0f || relative.x > width - 1 || relative.z > height - 1) { + return -1; + } + return src[(int)glm::round(relative.z) * width + (int)glm::round(relative.x)]; +} + +QVector& Heightfield::getMaterials() { + return _material->getMaterials(); +} + +bool Heightfield::contains(const glm::vec3& point) { + if (!_height) { + return false; + } + int width = _height->getWidth(); + const QVector& contents = _height->getContents(); + const quint16* src = contents.constData(); + int height = contents.size() / width; + int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION; + int innerHeight = height - HeightfieldHeight::HEIGHT_EXTENSION; + int highestX = innerWidth + HeightfieldHeight::HEIGHT_BORDER; + int highestZ = innerHeight + HeightfieldHeight::HEIGHT_BORDER; + + glm::vec3 relative = glm::inverse(getRotation()) * (point - getTranslation()) * glm::vec3(innerWidth / getScale(), + numeric_limits::max() / (getScale() * _aspectY), innerHeight / (getScale() * _aspectZ)); + if (relative.x < 0.0f || relative.y < 0.0f || relative.z < 0.0f || relative.x > innerWidth || + relative.y > numeric_limits::max() || relative.z > innerHeight) { + return false; + } + relative.x += HeightfieldHeight::HEIGHT_BORDER; + relative.z += HeightfieldHeight::HEIGHT_BORDER; + + // find the bounds of the cell containing the point and the shared vertex heights + glm::vec3 floors = glm::floor(relative); + glm::vec3 ceils = glm::ceil(relative); + glm::vec3 fracts = glm::fract(relative); + int floorX = qMin(qMax((int)floors.x, HeightfieldHeight::HEIGHT_BORDER), highestX); + int floorZ = qMin(qMax((int)floors.z, HeightfieldHeight::HEIGHT_BORDER), highestZ); + int ceilX = qMin(qMax((int)ceils.x, HeightfieldHeight::HEIGHT_BORDER), highestX); + int ceilZ = qMin(qMax((int)ceils.z, HeightfieldHeight::HEIGHT_BORDER), highestZ); + 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); + } + if (interpolatedHeight == 0.0f) { + return false; // ignore zero values + } + + // compare + return relative.y <= interpolatedHeight; +} + +bool Heightfield::intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal) { + int width = _height->getWidth(); + const QVector& contents = _height->getContents(); + const quint16* src = contents.constData(); + int height = contents.size() / width; + int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION; + int innerHeight = height - HeightfieldHeight::HEIGHT_EXTENSION; + int highestX = innerWidth + HeightfieldHeight::HEIGHT_BORDER; + int highestZ = innerHeight + HeightfieldHeight::HEIGHT_BORDER; + + glm::quat inverseRotation = glm::inverse(getRotation()); + glm::vec3 inverseScale(innerWidth / getScale(), numeric_limits::max() / (getScale() * _aspectY), + innerHeight / (getScale() * _aspectZ)); + glm::vec3 direction = end - start; + glm::vec3 dir = inverseRotation * direction * inverseScale; + glm::vec3 entry = inverseRotation * (start - getTranslation()) * inverseScale; + + float boundsDistance; + if (!Box(glm::vec3(), glm::vec3((float)innerWidth, (float)numeric_limits::max(), + (float)innerHeight)).findRayIntersection(entry, dir, boundsDistance) || boundsDistance > 1.0f) { + return false; + } + entry += dir * boundsDistance; + + const float DISTANCE_THRESHOLD = 0.001f; + if (glm::abs(entry.x - 0.0f) < DISTANCE_THRESHOLD) { + normal = getRotation() * glm::vec3(-1.0f, 0.0f, 0.0f); + distance = boundsDistance; + return true; + + } else if (glm::abs(entry.x - innerWidth) < DISTANCE_THRESHOLD) { + normal = getRotation() * glm::vec3(1.0f, 0.0f, 0.0f); + distance = boundsDistance; + return true; + + } else if (glm::abs(entry.y - 0.0f) < DISTANCE_THRESHOLD) { + normal = getRotation() * glm::vec3(0.0f, -1.0f, 0.0f); + distance = boundsDistance; + return true; + + } else if (glm::abs(entry.y - numeric_limits::max()) < DISTANCE_THRESHOLD) { + normal = getRotation() * glm::vec3(0.0f, 1.0f, 0.0f); + distance = boundsDistance; + return true; + + } else if (glm::abs(entry.z - 0.0f) < DISTANCE_THRESHOLD) { + normal = getRotation() * glm::vec3(0.0f, 0.0f, -1.0f); + distance = boundsDistance; + return true; + + } else if (glm::abs(entry.z - innerHeight) < DISTANCE_THRESHOLD) { + normal = getRotation() * glm::vec3(0.0f, 0.0f, 1.0f); + distance = boundsDistance; + return true; + } + + entry.x += HeightfieldHeight::HEIGHT_BORDER; + entry.z += HeightfieldHeight::HEIGHT_BORDER; + glm::vec3 floors = glm::floor(entry); + glm::vec3 ceils = glm::ceil(entry); + if (floors.x == ceils.x) { + if (dir.x > 0.0f) { + ceils.x += 1.0f; + } else { + floors.x -= 1.0f; + } + } + if (floors.z == ceils.z) { + if (dir.z > 0.0f) { + ceils.z += 1.0f; + } else { + floors.z -= 1.0f; + } + } + + glm::vec3 normalScale(1.0f / (inverseScale.y * inverseScale.z), 1.0f / (inverseScale.x * inverseScale.z), + 1.0f / (inverseScale.x * inverseScale.y)); + + bool withinBounds = true; + float accumulatedDistance = boundsDistance; + while (withinBounds && accumulatedDistance <= 1.0f) { + // find the heights at the corners of the current cell + int floorX = qMin(qMax((int)floors.x, HeightfieldHeight::HEIGHT_BORDER), highestX); + int floorZ = qMin(qMax((int)floors.z, HeightfieldHeight::HEIGHT_BORDER), highestZ); + int ceilX = qMin(qMax((int)ceils.x, HeightfieldHeight::HEIGHT_BORDER), highestX); + int ceilZ = qMin(qMax((int)ceils.z, HeightfieldHeight::HEIGHT_BORDER), highestZ); + float upperLeft = src[floorZ * width + floorX]; + float upperRight = src[floorZ * width + ceilX]; + float lowerLeft = src[ceilZ * width + floorX]; + float lowerRight = src[ceilZ * width + ceilX]; + + // find the distance to the next x coordinate + float xDistance = FLT_MAX; + if (dir.x > 0.0f) { + xDistance = (ceils.x - entry.x) / dir.x; + } else if (dir.x < 0.0f) { + xDistance = (floors.x - entry.x) / dir.x; + } + + // and the distance to the next z coordinate + float zDistance = FLT_MAX; + if (dir.z > 0.0f) { + zDistance = (ceils.z - entry.z) / dir.z; + } else if (dir.z < 0.0f) { + zDistance = (floors.z - entry.z) / dir.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 * dir; + withinBounds = (exit.y >= 0.0f && exit.y <= numeric_limits::max()); + if (exitDistance == xDistance) { + if (dir.x > 0.0f) { + nextFloors.x += 1.0f; + withinBounds &= (nextCeils.x += 1.0f) <= highestX; + } else { + withinBounds &= (nextFloors.x -= 1.0f) >= HeightfieldHeight::HEIGHT_BORDER; + nextCeils.x -= 1.0f; + } + } + if (exitDistance == zDistance) { + if (dir.z > 0.0f) { + nextFloors.z += 1.0f; + withinBounds &= (nextCeils.z += 1.0f) <= highestZ; + } else { + withinBounds &= (nextFloors.z -= 1.0f) >= HeightfieldHeight::HEIGHT_BORDER; + 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, dir); + if (lowerProduct != 0.0f) { + float planeDistance = -glm::dot(lowerNormal, relativeEntry) / lowerProduct; + glm::vec3 intersection = relativeEntry + planeDistance * dir; + if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && + intersection.z >= intersection.x) { + distance = accumulatedDistance + planeDistance; + normal = glm::normalize(getRotation() * (lowerNormal * normalScale)); + return true; + } + } + + // then the one with the X+ segment + glm::vec3 upperNormal(upperLeft - upperRight, 1.0f, upperRight - lowerRight); + float upperProduct = glm::dot(upperNormal, dir); + if (upperProduct != 0.0f) { + float planeDistance = -glm::dot(upperNormal, relativeEntry) / upperProduct; + glm::vec3 intersection = relativeEntry + planeDistance * dir; + if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && + intersection.x >= intersection.z) { + distance = accumulatedDistance + planeDistance; + normal = glm::normalize(getRotation() * (upperNormal * normalScale)); + return true; + } + } + + // no joy; continue on our way + entry = exit; + floors = nextFloors; + ceils = nextCeils; + accumulatedDistance += exitDistance; + } + + return false; +} + +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(glm::vec3(), extent)); +} diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h new file mode 100644 index 0000000000..7fe32b56a6 --- /dev/null +++ b/libraries/metavoxels/src/Spanner.h @@ -0,0 +1,536 @@ +// +// 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 + +#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(); + + /// Checks whether this is a heightfield. + virtual bool isHeightfield() const; + + /// Finds the height at the specified location, or returns -FLT_MAX for none. + virtual float getHeight(const glm::vec3& location) const; + + /// Finds the intersection between the described ray and this spanner. + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + + /// Attempts to paint on the spanner. + /// \return the modified spanner, or this if no modification was performed + virtual Spanner* paintMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material, + const QColor& color); + + /// Attempts to modify the spanner's height. + /// \return the modified spanner, or this if no modification was performed + virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height); + + /// Attempts to clear and fetch part of the spanner's height. + /// \param heightfield the heightfield to populate + /// \return the modified spanner, or this if no modification was performed + virtual Spanner* clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield); + + /// 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& 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 _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: + + Q_INVOKABLE SpannerRenderer(); + + virtual void init(Spanner* spanner); + virtual void simulate(float deltaTime); + virtual void render(bool cursor = false); + 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; +}; + +/// Base class for heightfield data blocks. +class HeightfieldData : public DataBlock { +public: + + static const int SHARED_EDGE; + + HeightfieldData(int width = 0); + + int getWidth() const { return _width; } + +protected: + + int _width; +}; + +typedef QExplicitlySharedDataPointer HeightfieldHeightPointer; + +/// A block of height data associated with a heightfield. +class HeightfieldHeight : public HeightfieldData { +public: + + static const int HEIGHT_BORDER; + static const int HEIGHT_EXTENSION; + + HeightfieldHeight(int width, const QVector& contents); + HeightfieldHeight(Bitstream& in, int bytes); + HeightfieldHeight(Bitstream& in, int bytes, const HeightfieldHeightPointer& reference); + + QVector& getContents() { return _contents; } + + void write(Bitstream& out); + void writeDelta(Bitstream& out, const HeightfieldHeightPointer& reference); + +private: + + void read(Bitstream& in, int bytes); + + QVector _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); + +/// Allows editing heightfield height blocks. +class HeightfieldHeightEditor : public QWidget { + Q_OBJECT + Q_PROPERTY(HeightfieldHeightPointer height MEMBER _height WRITE setHeight NOTIFY heightChanged USER true) + +public: + + HeightfieldHeightEditor(QWidget* parent = NULL); + + const HeightfieldHeightPointer& getHeight() const { return _height; } + +signals: + + void heightChanged(const HeightfieldHeightPointer& height); + +public slots: + + void setHeight(const HeightfieldHeightPointer& height); + +private slots: + + void select(); + void clear(); + +private: + + HeightfieldHeightPointer _height; + + QPushButton* _select; + QPushButton* _clear; +}; + +typedef QExplicitlySharedDataPointer 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); + + QByteArray& getContents() { return _contents; } + + void write(Bitstream& out); + void writeDelta(Bitstream& out, const HeightfieldColorPointer& reference); + +private: + + void read(Bitstream& in, int bytes); + + 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); + +/// Allows editing heightfield color blocks. +class HeightfieldColorEditor : public QWidget { + Q_OBJECT + Q_PROPERTY(HeightfieldColorPointer color MEMBER _color WRITE setColor NOTIFY colorChanged USER true) + +public: + + HeightfieldColorEditor(QWidget* parent = NULL); + + const HeightfieldColorPointer& getColor() const { return _color; } + +signals: + + void colorChanged(const HeightfieldColorPointer& color); + +public slots: + + void setColor(const HeightfieldColorPointer& color); + +private slots: + + void select(); + void clear(); + +private: + + HeightfieldColorPointer _color; + + QPushButton* _select; + QPushButton* _clear; +}; + +typedef QExplicitlySharedDataPointer HeightfieldMaterialPointer; + +/// A block of material data associated with a heightfield. +class HeightfieldMaterial : public HeightfieldData { +public: + + HeightfieldMaterial(int width, const QByteArray& contents, const QVector& materials); + HeightfieldMaterial(Bitstream& in, int bytes); + HeightfieldMaterial(Bitstream& in, int bytes, const HeightfieldMaterialPointer& reference); + + QByteArray& getContents() { return _contents; } + QVector& getMaterials() { return _materials; } + + void write(Bitstream& out); + void writeDelta(Bitstream& out, const HeightfieldMaterialPointer& reference); + +private: + + void read(Bitstream& in, int bytes); + + QByteArray _contents; + QVector _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 DESIGNABLE false) + +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; } + + virtual bool isHeightfield() const; + + virtual float getHeight(const glm::vec3& location) const; + + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + + virtual Spanner* paintMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material, + const QColor& color); + + virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height); + + virtual Spanner* clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield); + + virtual bool hasOwnColors() const; + virtual bool hasOwnMaterials() const; + virtual QRgb getColorAt(const glm::vec3& point); + virtual int getMaterialAt(const glm::vec3& point); + virtual QVector& getMaterials(); + + 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); + 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 diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 6113cadb06..81b3a1328b 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -131,7 +131,7 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) { void AddressManager::handleLookupString(const QString& lookupString) { if (!lookupString.isEmpty()) { // make this a valid hifi URL and handle it off to handleUrl - QString sanitizedString = lookupString; + QString sanitizedString = lookupString.trimmed(); QUrl lookupURL; if (!lookupString.startsWith('/')) { diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index e200d82e9d..b1c47c0ebf 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -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: diff --git a/libraries/octree/src/CoverageMap.cpp b/libraries/octree/src/CoverageMap.cpp index 06ad509d15..320e7f60ef 100644 --- a/libraries/octree/src/CoverageMap.cpp +++ b/libraries/octree/src/CoverageMap.cpp @@ -24,7 +24,7 @@ bool CoverageMap::wantDebugging = false; const int MAX_POLYGONS_PER_REGION = 50; -const BoundingBox CoverageMap::ROOT_BOUNDING_BOX = BoundingBox(glm::vec2(-1.f,-1.f), glm::vec2(2.f,2.f)); +const BoundingBox CoverageMap::ROOT_BOUNDING_BOX = BoundingBox(glm::vec2(-1.0f,-1.0f), glm::vec2(2.0f,2.0f)); // Coverage Map's polygon coordinates are from -1 to 1 in the following mapping to screen space. // diff --git a/libraries/octree/src/CoverageMapV2.cpp b/libraries/octree/src/CoverageMapV2.cpp index 9f0b58bfca..a29231fe26 100644 --- a/libraries/octree/src/CoverageMapV2.cpp +++ b/libraries/octree/src/CoverageMapV2.cpp @@ -23,7 +23,7 @@ int CoverageMapV2::_checkMapRootCalls = 0; int CoverageMapV2::_notAllInView = 0; bool CoverageMapV2::wantDebugging = false; -const BoundingBox CoverageMapV2::ROOT_BOUNDING_BOX = BoundingBox(glm::vec2(-1.f,-1.f), glm::vec2(2.f,2.f)); +const BoundingBox CoverageMapV2::ROOT_BOUNDING_BOX = BoundingBox(glm::vec2(-1.0f,-1.0f), glm::vec2(2.0f,2.0f)); // Coverage Map's polygon coordinates are from -1 to 1 in the following mapping to screen space. // diff --git a/libraries/octree/src/ViewFrustum.h b/libraries/octree/src/ViewFrustum.h index 1fd306617b..a5b1d33f19 100644 --- a/libraries/octree/src/ViewFrustum.h +++ b/libraries/octree/src/ViewFrustum.h @@ -27,8 +27,8 @@ const float DEFAULT_KEYHOLE_RADIUS = 3.0f; const float DEFAULT_FIELD_OF_VIEW_DEGREES = 45.0f; -const float DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES = 30.f; -const float DEFAULT_ASPECT_RATIO = 16.f/9.f; +const float DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES = 30.0f; +const float DEFAULT_ASPECT_RATIO = 16.0f/9.0f; const float DEFAULT_NEAR_CLIP = 0.08f; const float DEFAULT_FAR_CLIP = TREE_SCALE; diff --git a/libraries/physics/src/CollisionInfo.cpp b/libraries/physics/src/CollisionInfo.cpp index 20ca543d90..9add847980 100644 --- a/libraries/physics/src/CollisionInfo.cpp +++ b/libraries/physics/src/CollisionInfo.cpp @@ -20,11 +20,11 @@ CollisionInfo::CollisionInfo() : _intData(0), _shapeA(NULL), _shapeB(NULL), - _damping(0.f), - _elasticity(1.f), - _contactPoint(0.f), - _penetration(0.f), - _addedVelocity(0.f) { + _damping(0.0f), + _elasticity(1.0f), + _contactPoint(0.0f), + _penetration(0.0f), + _addedVelocity(0.0f) { } quint64 CollisionInfo::getShapePairKey() const { diff --git a/libraries/physics/src/ListShape.cpp b/libraries/physics/src/ListShape.cpp index cf548d3430..319c3a1287 100644 --- a/libraries/physics/src/ListShape.cpp +++ b/libraries/physics/src/ListShape.cpp @@ -73,11 +73,11 @@ void ListShape::clear() { delete _subShapeEntries[i]._shape; } _subShapeEntries.clear(); - setBoundingRadius(0.f); + setBoundingRadius(0.0f); } void ListShape::computeBoundingRadius() { - float maxRadius = 0.f; + float maxRadius = 0.0f; for (int i = 0; i < _subShapeEntries.size(); ++i) { ListShapeEntry& entry = _subShapeEntries[i]; float radius = glm::length(entry._localPosition) + entry._shape->getBoundingRadius(); diff --git a/libraries/physics/src/Shape.h b/libraries/physics/src/Shape.h index 139f7719de..d029dddd87 100644 --- a/libraries/physics/src/Shape.h +++ b/libraries/physics/src/Shape.h @@ -39,8 +39,8 @@ public: static quint32 getNextID() { static quint32 nextID = 0; return ++nextID; } - Shape() : _type(UNKNOWN_SHAPE), _owningEntity(NULL), _boundingRadius(0.f), - _translation(0.f), _rotation(), _mass(MAX_SHAPE_MASS) { + Shape() : _type(UNKNOWN_SHAPE), _owningEntity(NULL), _boundingRadius(0.0f), + _translation(0.0f), _rotation(), _mass(MAX_SHAPE_MASS) { _id = getNextID(); } virtual ~Shape() { } @@ -87,20 +87,20 @@ public: protected: // these ctors are protected (used by derived classes only) Shape(Type type) : _type(type), _owningEntity(NULL), - _boundingRadius(0.f), _translation(0.f), + _boundingRadius(0.0f), _translation(0.0f), _rotation(), _mass(MAX_SHAPE_MASS) { _id = getNextID(); } Shape(Type type, const glm::vec3& position) : _type(type), _owningEntity(NULL), - _boundingRadius(0.f), _translation(position), + _boundingRadius(0.0f), _translation(position), _rotation(), _mass(MAX_SHAPE_MASS) { _id = getNextID(); } Shape(Type type, const glm::vec3& position, const glm::quat& rotation) : _type(type), _owningEntity(NULL), - _boundingRadius(0.f), _translation(position), + _boundingRadius(0.0f), _translation(position), _rotation(rotation), _mass(MAX_SHAPE_MASS) { _id = getNextID(); } diff --git a/libraries/physics/src/ShapeCollider.cpp b/libraries/physics/src/ShapeCollider.cpp index f94e06bf26..a7a4e34518 100644 --- a/libraries/physics/src/ShapeCollider.cpp +++ b/libraries/physics/src/ShapeCollider.cpp @@ -1139,7 +1139,7 @@ bool sphereAACube_StarkAngles(const glm::vec3& sphereCenter, float sphereRadius, glm::vec3 surfaceB = cubeCenter - (0.5f * cubeSide / maxBA) * BA; // collision happens when "vector to surfaceA from surfaceB" dots with BA to produce a positive value glm::vec3 surfaceAB = surfaceA - surfaceB; - if (glm::dot(surfaceAB, BA) > 0.f) { + if (glm::dot(surfaceAB, BA) > 0.0f) { CollisionInfo* collision = collisions.getNewCollision(); if (collision) { // penetration is parallel to box side direction diff --git a/libraries/script-engine/src/Vec3.cpp b/libraries/script-engine/src/Vec3.cpp index bab63bbd4e..cb9ad045ff 100644 --- a/libraries/script-engine/src/Vec3.cpp +++ b/libraries/script-engine/src/Vec3.cpp @@ -15,6 +15,9 @@ #include "Vec3.h" +glm::vec3 Vec3::reflect(const glm::vec3& v1, const glm::vec3& v2) { + return glm::reflect(v1, v2); +} glm::vec3 Vec3::cross(const glm::vec3& v1, const glm::vec3& v2) { return glm::cross(v1,v2); } diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h index 3f7d5476a2..2a2e145e8e 100644 --- a/libraries/script-engine/src/Vec3.h +++ b/libraries/script-engine/src/Vec3.h @@ -25,6 +25,7 @@ class Vec3 : public QObject { Q_OBJECT public slots: + glm::vec3 reflect(const glm::vec3& v1, const glm::vec3& v2); glm::vec3 cross(const glm::vec3& v1, const glm::vec3& v2); float dot(const glm::vec3& v1, const glm::vec3& v2); glm::vec3 multiply(const glm::vec3& v1, float f); diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index 59ac84a856..a57aa75e9b 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -72,27 +72,27 @@ int unpackFloatVec3FromSignedTwoByteFixed(const unsigned char* sourceBuffer, glm int packFloatAngleToTwoByte(unsigned char* buffer, float degrees) { - const float ANGLE_CONVERSION_RATIO = (std::numeric_limits::max() / 360.f); + const float ANGLE_CONVERSION_RATIO = (std::numeric_limits::max() / 360.0f); - uint16_t angleHolder = floorf((degrees + 180.f) * ANGLE_CONVERSION_RATIO); + uint16_t angleHolder = floorf((degrees + 180.0f) * ANGLE_CONVERSION_RATIO); memcpy(buffer, &angleHolder, sizeof(uint16_t)); return sizeof(uint16_t); } int unpackFloatAngleFromTwoByte(const uint16_t* byteAnglePointer, float* destinationPointer) { - *destinationPointer = (*byteAnglePointer / (float) std::numeric_limits::max()) * 360.f - 180.f; + *destinationPointer = (*byteAnglePointer / (float) std::numeric_limits::max()) * 360.0f - 180.0f; return sizeof(uint16_t); } int packOrientationQuatToBytes(unsigned char* buffer, const glm::quat& quatInput) { glm::quat quatNormalized = glm::normalize(quatInput); - const float QUAT_PART_CONVERSION_RATIO = (std::numeric_limits::max() / 2.f); + const float QUAT_PART_CONVERSION_RATIO = (std::numeric_limits::max() / 2.0f); uint16_t quatParts[4]; - quatParts[0] = floorf((quatNormalized.x + 1.f) * QUAT_PART_CONVERSION_RATIO); - quatParts[1] = floorf((quatNormalized.y + 1.f) * QUAT_PART_CONVERSION_RATIO); - quatParts[2] = floorf((quatNormalized.z + 1.f) * QUAT_PART_CONVERSION_RATIO); - quatParts[3] = floorf((quatNormalized.w + 1.f) * QUAT_PART_CONVERSION_RATIO); + quatParts[0] = floorf((quatNormalized.x + 1.0f) * QUAT_PART_CONVERSION_RATIO); + quatParts[1] = floorf((quatNormalized.y + 1.0f) * QUAT_PART_CONVERSION_RATIO); + quatParts[2] = floorf((quatNormalized.z + 1.0f) * QUAT_PART_CONVERSION_RATIO); + quatParts[3] = floorf((quatNormalized.w + 1.0f) * QUAT_PART_CONVERSION_RATIO); memcpy(buffer, &quatParts, sizeof(quatParts)); return sizeof(quatParts); @@ -102,10 +102,10 @@ int unpackOrientationQuatFromBytes(const unsigned char* buffer, glm::quat& quatO uint16_t quatParts[4]; memcpy(&quatParts, buffer, sizeof(quatParts)); - quatOutput.x = ((quatParts[0] / (float) std::numeric_limits::max()) * 2.f) - 1.f; - quatOutput.y = ((quatParts[1] / (float) std::numeric_limits::max()) * 2.f) - 1.f; - quatOutput.z = ((quatParts[2] / (float) std::numeric_limits::max()) * 2.f) - 1.f; - quatOutput.w = ((quatParts[3] / (float) std::numeric_limits::max()) * 2.f) - 1.f; + quatOutput.x = ((quatParts[0] / (float) std::numeric_limits::max()) * 2.0f) - 1.0f; + quatOutput.y = ((quatParts[1] / (float) std::numeric_limits::max()) * 2.0f) - 1.0f; + quatOutput.z = ((quatParts[2] / (float) std::numeric_limits::max()) * 2.0f) - 1.0f; + quatOutput.w = ((quatParts[3] / (float) std::numeric_limits::max()) * 2.0f) - 1.0f; return sizeof(quatParts); } diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index cb2e829811..1b6472a18f 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -131,9 +131,9 @@ bool findSphereDiskPenetration(const glm::vec3& sphereCenter, float sphereRadius if (glm::length(localCenter - axialOffset) < diskRadius) { // yes, hit the disk penetration = (std::fabs(axialDistance) - (sphereRadius + 0.5f * diskThickness) ) * diskNormal; - if (axialDistance < 0.f) { + if (axialDistance < 0.0f) { // hit the backside of the disk, so negate penetration vector - penetration *= -1.f; + penetration *= -1.0f; } return true; } diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 595d7078f8..c376836d24 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -46,31 +46,50 @@ quint64 usecTimestampNow(bool wantDebug) { TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * 1000; // ms to usec timestampTimer.start(); usecTimestampNowIsInitialized = true; - } else { - // We've seen the QElapsedTimer begin to introduce dramatic errors if it's been - // continuously running for a long time. So we will periodically reset it. - const quint64 SECS_TO_NSECS = 1000000000; - const quint64 RESET_AFTER_ELAPSED_SECS = 60 * 60; // 1 hour: 60 minutes * 60 seconds - const quint64 RESET_AFTER_ELAPSED_NSECS = RESET_AFTER_ELAPSED_SECS * SECS_TO_NSECS; - const quint64 nsecsElapsed = timestampTimer.nsecsElapsed(); - if (nsecsElapsed > RESET_AFTER_ELAPSED_NSECS) { - quint64 msecsElapsed = timestampTimer.restart(); - quint64 usecsElapsed = nsecsElapsed / 1000; // nsec to usec - TIME_REFERENCE += usecsElapsed; - if (wantDebug) { - qDebug() << "usecTimestampNow() - resetting QElapsedTimer. "; - qDebug() << " RESET_AFTER_ELAPSED_NSECS:" << RESET_AFTER_ELAPSED_NSECS; - qDebug() << " nsecsElapsed:" << nsecsElapsed; - qDebug() << " msecsElapsed:" << msecsElapsed; - qDebug() << " usecsElapsed:" << usecsElapsed; - } - } } + quint64 now; quint64 nsecsElapsed = timestampTimer.nsecsElapsed(); quint64 usecsElapsed = nsecsElapsed / 1000; // nsec to usec - quint64 now = TIME_REFERENCE + usecsElapsed + ::usecTimestampNowAdjust; + // QElapsedTimer may not advance if the CPU has gone to sleep. In which case it + // will begin to deviate from real time. We detect that here, and reset if necessary + quint64 msecsCurrentTime = QDateTime::currentMSecsSinceEpoch(); + quint64 msecsEstimate = (TIME_REFERENCE + usecsElapsed) / 1000; // usecs to msecs + int possibleSkew = msecsEstimate - msecsCurrentTime; + const int TOLERANCE = 10000; // up to 10 seconds of skew is tolerated + if (abs(possibleSkew) > TOLERANCE) { + // reset our TIME_REFERENCE and timer + TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * 1000; // ms to usec + timestampTimer.restart(); + now = TIME_REFERENCE + ::usecTimestampNowAdjust; + + if (wantDebug) { + qDebug() << "usecTimestampNow() - resetting QElapsedTimer. "; + qDebug() << " msecsCurrentTime:" << msecsCurrentTime; + qDebug() << " msecsEstimate:" << msecsEstimate; + qDebug() << " possibleSkew:" << possibleSkew; + qDebug() << " TOLERANCE:" << TOLERANCE; + + qDebug() << " nsecsElapsed:" << nsecsElapsed; + qDebug() << " usecsElapsed:" << usecsElapsed; + + QDateTime currentLocalTime = QDateTime::currentDateTime(); + + quint64 msecsNow = now / 1000; // usecs to msecs + QDateTime nowAsString; + nowAsString.setMSecsSinceEpoch(msecsNow); + + qDebug() << " now:" << now; + qDebug() << " msecsNow:" << msecsNow; + + qDebug() << " nowAsString:" << nowAsString.toString("yyyy-MM-dd hh:mm:ss.zzz"); + qDebug() << " currentLocalTime:" << currentLocalTime.toString("yyyy-MM-dd hh:mm:ss.zzz"); + } + } else { + now = TIME_REFERENCE + usecsElapsed + ::usecTimestampNowAdjust; + } + if (wantDebug) { QDateTime currentLocalTime = QDateTime::currentDateTime(); @@ -98,7 +117,7 @@ quint64 usecTimestampNow(bool wantDebug) { } float randFloat() { - return (rand() % 10000)/10000.f; + return (rand() % 10000)/10000.0f; } int randIntInRange (int min, int max) { @@ -106,7 +125,7 @@ int randIntInRange (int min, int max) { } float randFloatInRange (float min,float max) { - return min + ((rand() % 10000)/10000.f * (max-min)); + return min + ((rand() % 10000)/10000.0f * (max-min)); } float randomSign() { @@ -501,8 +520,8 @@ int removeFromSortedArrays(void* value, void** valueArray, float* keyArray, int* return -1; // error case } -float SMALL_LIMIT = 10.f; -float LARGE_LIMIT = 1000.f; +float SMALL_LIMIT = 10.0f; +float LARGE_LIMIT = 1000.0f; int packFloatRatioToTwoByte(unsigned char* buffer, float ratio) { // if the ratio is less than 10, then encode it as a positive number scaled from 0 to int16::max() diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 3fe3c3d1ec..01edcc01b9 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -49,14 +49,14 @@ static const float ONE_HALF = 0.5f; static const float ONE_THIRD = 0.333333f; static const float PI = 3.14159265358979f; -static const float TWO_PI = 2.f * PI; +static const float TWO_PI = 2.0f * PI; static const float PI_OVER_TWO = ONE_HALF * PI; static const float RADIANS_PER_DEGREE = PI / 180.0f; static const float DEGREES_PER_RADIAN = 180.0f / PI; static const float EPSILON = 0.000001f; //smallish positive number - used as margin of error for some computations -static const float SQUARE_ROOT_OF_2 = (float)sqrt(2.f); -static const float SQUARE_ROOT_OF_3 = (float)sqrt(3.f); +static const float SQUARE_ROOT_OF_2 = (float)sqrt(2.0f); +static const float SQUARE_ROOT_OF_3 = (float)sqrt(3.0f); static const float METERS_PER_DECIMETER = 0.1f; static const float METERS_PER_CENTIMETER = 0.01f; static const float METERS_PER_MILLIMETER = 0.001f; diff --git a/libraries/shared/src/StreamUtils.cpp b/libraries/shared/src/StreamUtils.cpp index 1723068eee..85b800efbe 100644 --- a/libraries/shared/src/StreamUtils.cpp +++ b/libraries/shared/src/StreamUtils.cpp @@ -86,7 +86,7 @@ std::ostream& operator<<(std::ostream& s, const SphereShape& sphere) { std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule) { s << "{type='capsule', center=" << capsule.getPosition() << ", radius=" << capsule.getRadius() - << ", length=" << (2.f * capsule.getHalfHeight()) + << ", length=" << (2.0f * capsule.getHalfHeight()) << ", begin=" << capsule.getStartPoint() << ", end=" << capsule.getEndPoint() << "}"; diff --git a/libraries/shared/src/Transform.cpp b/libraries/shared/src/Transform.cpp index 9945091ed0..a00761fd50 100644 --- a/libraries/shared/src/Transform.cpp +++ b/libraries/shared/src/Transform.cpp @@ -15,28 +15,28 @@ void Transform::evalRotationScale(Quat& rotation, Vec3& scale, const Mat3& rotationScaleMatrix) { const float ACCURACY_THREASHOLD = 0.00001f; - // Following technique taken from: - // http://callumhay.blogspot.com/2010/10/decomposing-affine-transforms.html - // Extract the rotation component - this is done using polar decompostion, where - // we successively average the matrix with its inverse transpose until there is - // no/a very small difference between successive averages - float norm; - int count = 0; - Mat3 rotationMat = rotationScaleMatrix; - do { - Mat3 currInvTranspose = glm::inverse(glm::transpose(rotationMat)); - - Mat3 nextRotation = 0.5f * (rotationMat + currInvTranspose); - - norm = 0.0; - for (int i = 0; i < 3; i++) { - float n = static_cast( - fabs(rotationMat[0][i] - nextRotation[0][i]) + - fabs(rotationMat[1][i] - nextRotation[1][i]) + - fabs(rotationMat[2][i] - nextRotation[2][i])); - norm = (norm > n ? norm : n); - } - rotationMat = nextRotation; + // Following technique taken from: + // http://callumhay.blogspot.com/2010/10/decomposing-affine-transforms.html + // Extract the rotation component - this is done using polar decompostion, where + // we successively average the matrix with its inverse transpose until there is + // no/a very small difference between successive averages + float norm; + int count = 0; + Mat3 rotationMat = rotationScaleMatrix; + do { + Mat3 currInvTranspose = glm::inverse(glm::transpose(rotationMat)); + + Mat3 nextRotation = 0.5f * (rotationMat + currInvTranspose); + + norm = 0.0; + for (int i = 0; i < 3; i++) { + float n = static_cast( + fabs(rotationMat[0][i] - nextRotation[0][i]) + + fabs(rotationMat[1][i] - nextRotation[1][i]) + + fabs(rotationMat[2][i] - nextRotation[2][i])); + norm = (norm > n ? norm : n); + } + rotationMat = nextRotation; } while (count < 100 && norm > ACCURACY_THREASHOLD); @@ -49,14 +49,14 @@ void Transform::evalRotationScale(Quat& rotation, Vec3& scale, const Mat3& rotat Mat3 matRot( rotationScaleMatrix[0] / scale.x, rotationScaleMatrix[1] / scale.y, - rotationScaleMatrix[2] / scale.z); - - // Beware!!! needs to detect for the case there is a negative scale - // Based on the determinant sign we just can flip the scale sign of one component: we choose X axis - float determinant = glm::determinant(matRot); - if (determinant < 0.f) { - scale.x = -scale.x; - matRot[0] *= -1.f; + rotationScaleMatrix[2] / scale.z); + + // Beware!!! needs to detect for the case there is a negative scale + // Based on the determinant sign we just can flip the scale sign of one component: we choose X axis + float determinant = glm::determinant(matRot); + if (determinant < 0.0f) { + scale.x = -scale.x; + matRot[0] *= -1.0f; } // Beware: even though the matRot is supposed to be normalized at that point, diff --git a/libraries/shared/src/Transform.h b/libraries/shared/src/Transform.h index 3c668adcd6..5ce5667a81 100644 --- a/libraries/shared/src/Transform.h +++ b/libraries/shared/src/Transform.h @@ -292,13 +292,13 @@ inline Transform::Mat4& Transform::getMatrix(Transform::Mat4& result) const { rot[2] *= _scale.z; } - result[0] = Vec4(rot[0], 0.f); - result[1] = Vec4(rot[1], 0.f); - result[2] = Vec4(rot[2], 0.f); + result[0] = Vec4(rot[0], 0.0f); + result[1] = Vec4(rot[1], 0.0f); + result[2] = Vec4(rot[2], 0.0f); } else { - result[0] = Vec4(_scale.x, 0.f, 0.f, 0.f); - result[1] = Vec4(0.f, _scale.y, 0.f, 0.f); - result[2] = Vec4(0.f, 0.f, _scale.z, 0.f); + result[0] = Vec4(_scale.x, 0.0f, 0.0f, 0.0f); + result[1] = Vec4(0.0f, _scale.y, 0.0f, 0.0f); + result[2] = Vec4(0.0f, 0.0f, _scale.z, 0.0f); } result[3] = Vec4(_translation, 1.0f); @@ -313,7 +313,7 @@ inline Transform::Mat4& Transform::getInverseMatrix(Transform::Mat4& result) con inline void Transform::evalFromRawMatrix(const Mat4& matrix) { // for now works only in the case of TRS transformation - if ((matrix[0][3] == 0) && (matrix[1][3] == 0) && (matrix[2][3] == 0) && (matrix[3][3] == 1.f)) { + if ((matrix[0][3] == 0) && (matrix[1][3] == 0) && (matrix[2][3] == 0) && (matrix[3][3] == 1.0f)) { setTranslation(Vec3(matrix[3])); evalFromRawMatrix(Mat3(matrix)); } diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index 92f8d0568c..0609d7bd01 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -445,6 +445,9 @@ bool MetavoxelTests::run() { // seed the random number generator so that our tests are reproducible srand(0xBAAAAABE); + // register our test attribute + AttributePointer testAttribute = AttributeRegistry::getInstance()->registerAttribute(new FloatAttribute("testAttribute")); + // check for an optional command line argument specifying a single test QStringList arguments = this->arguments(); int test = (arguments.size() > 1) ? arguments.at(1).toInt() : 0; @@ -582,8 +585,8 @@ public: }; RandomVisitor::RandomVisitor() : - MetavoxelVisitor(QVector(), - QVector() << AttributeRegistry::getInstance()->getColorAttribute()), + MetavoxelVisitor(QVector(), QVector() << + AttributeRegistry::getInstance()->getAttribute("testAttribute")), leafCount(0) { } @@ -594,8 +597,7 @@ int RandomVisitor::visit(MetavoxelInfo& info) { if (info.size > MAXIMUM_LEAF_SIZE || (info.size > MINIMUM_LEAF_SIZE && randomBoolean())) { return DEFAULT_ORDER; } - info.outputValues[0] = OwnedAttributeValue(_outputs.at(0), encodeInline(qRgb(randomColorValue(), - randomColorValue(), randomColorValue()))); + info.outputValues[0] = OwnedAttributeValue(_outputs.at(0), encodeInline(randFloat())); leafCount++; return STOP_RECURSION; } @@ -812,8 +814,8 @@ private: }; MutateVisitor::MutateVisitor() : - MetavoxelVisitor(QVector(), - QVector() << AttributeRegistry::getInstance()->getColorAttribute()), + MetavoxelVisitor(QVector(), QVector() << + AttributeRegistry::getInstance()->getAttribute("testAttribute")), _mutationsRemaining(randIntInRange(2, 4)) { } @@ -824,8 +826,7 @@ int MutateVisitor::visit(MetavoxelInfo& info) { if (info.size > MAXIMUM_LEAF_SIZE || (info.size > MINIMUM_LEAF_SIZE && randomBoolean())) { return encodeRandomOrder(); } - info.outputValues[0] = OwnedAttributeValue(_outputs.at(0), encodeInline(qRgb(randomColorValue(), - randomColorValue(), randomColorValue()))); + info.outputValues[0] = OwnedAttributeValue(_outputs.at(0), encodeInline(randFloat())); _mutationsRemaining--; metavoxelMutationsPerformed++; return STOP_RECURSION; diff --git a/tests/physics/src/CollisionInfoTests.cpp b/tests/physics/src/CollisionInfoTests.cpp index ffc7e94cc4..0aa84c3afa 100644 --- a/tests/physics/src/CollisionInfoTests.cpp +++ b/tests/physics/src/CollisionInfoTests.cpp @@ -23,9 +23,9 @@ /* -static glm::vec3 xAxis(1.f, 0.f, 0.f); -static glm::vec3 xZxis(0.f, 1.f, 0.f); -static glm::vec3 xYxis(0.f, 0.f, 1.f); +static glm::vec3 xAxis(1.0f, 0.0f, 0.0f); +static glm::vec3 xZxis(0.0f, 1.0f, 0.0f); +static glm::vec3 xYxis(0.0f, 0.0f, 1.0f); void CollisionInfoTests::rotateThenTranslate() { CollisionInfo collision; @@ -34,7 +34,7 @@ void CollisionInfoTests::rotateThenTranslate() { collision._addedVelocity = xAxis + yAxis + zAxis; glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis); - float distance = 3.f; + float distance = 3.0f; glm::vec3 translation = distance * yAxis; collision.rotateThenTranslate(rotation, translation); @@ -73,7 +73,7 @@ void CollisionInfoTests::translateThenRotate() { collision._addedVelocity = xAxis + yAxis + zAxis; glm::quat rotation = glm::angleAxis( -PI_OVER_TWO, zAxis); - float distance = 3.f; + float distance = 3.0f; glm::vec3 translation = distance * yAxis; collision.translateThenRotate(translation, rotation); @@ -86,7 +86,7 @@ void CollisionInfoTests::translateThenRotate() { << std::endl; } - glm::vec3 expectedContactPoint = (1.f + distance) * xAxis; + glm::vec3 expectedContactPoint = (1.0f + distance) * xAxis; error = glm::distance(collision._contactPoint, expectedContactPoint); if (error > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ diff --git a/tests/physics/src/PhysicsTestUtil.h b/tests/physics/src/PhysicsTestUtil.h index c93545dd76..0e43084a05 100644 --- a/tests/physics/src/PhysicsTestUtil.h +++ b/tests/physics/src/PhysicsTestUtil.h @@ -17,9 +17,9 @@ #include -const glm::vec3 xAxis(1.f, 0.f, 0.f); -const glm::vec3 yAxis(0.f, 1.f, 0.f); -const glm::vec3 zAxis(0.f, 0.f, 1.f); +const glm::vec3 xAxis(1.0f, 0.0f, 0.0f); +const glm::vec3 yAxis(0.0f, 1.0f, 0.0f); +const glm::vec3 zAxis(0.0f, 0.0f, 1.0f); std::ostream& operator<<(std::ostream& s, const CollisionInfo& c); diff --git a/tests/physics/src/ShapeColliderTests.cpp b/tests/physics/src/ShapeColliderTests.cpp index 84335901bf..ff15a29e19 100644 --- a/tests/physics/src/ShapeColliderTests.cpp +++ b/tests/physics/src/ShapeColliderTests.cpp @@ -2342,7 +2342,7 @@ void ShapeColliderTests::rayMissesAACube() { //glm::vec3 cubeCenter(1.23f, 4.56f, 7.89f); //float cubeSide = 2.127f; glm::vec3 cubeCenter(0.0f); - float cubeSide = 2.f; + float cubeSide = 2.0f; AACubeShape cube(cubeSide, cubeCenter); float rayOffset = 3.796f; diff --git a/tests/physics/src/VerletShapeTests.cpp b/tests/physics/src/VerletShapeTests.cpp index df5cdc5c6b..c075fa9f85 100644 --- a/tests/physics/src/VerletShapeTests.cpp +++ b/tests/physics/src/VerletShapeTests.cpp @@ -37,7 +37,7 @@ void VerletShapeTests::setSpherePosition() { VerletPoint point; VerletSphereShape sphere(radius, &point); - point._position = glm::vec3(0.f); + point._position = glm::vec3(0.0f); float d = glm::distance(glm::vec3(0.0f), sphere.getTranslation()); if (d != 0.0f) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should be at origin" << std::endl; diff --git a/tests/shared/src/AngularConstraintTests.cpp b/tests/shared/src/AngularConstraintTests.cpp index 00916a7267..d7a7835655 100644 --- a/tests/shared/src/AngularConstraintTests.cpp +++ b/tests/shared/src/AngularConstraintTests.cpp @@ -47,7 +47,7 @@ void AngularConstraintTests::testHingeConstraint() { } } { // test just inside min edge of constraint - float angle = minAngle + 10.f * EPSILON; + float angle = minAngle + 10.0f * EPSILON; glm::quat rotation = glm::angleAxis(angle, yAxis); glm::quat newRotation = rotation; @@ -62,7 +62,7 @@ void AngularConstraintTests::testHingeConstraint() { } } { // test just inside max edge of constraint - float angle = maxAngle - 10.f * EPSILON; + float angle = maxAngle - 10.0f * EPSILON; glm::quat rotation = glm::angleAxis(angle, yAxis); glm::quat newRotation = rotation; diff --git a/voxel-edit/CMakeLists.txt b/voxel-edit/CMakeLists.txt deleted file mode 100644 index b61ee6d132..0000000000 --- a/voxel-edit/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -set(TARGET_NAME voxel-edit) - -setup_hifi_project() - -include_glm() - -link_hifi_libraries(networking octree shared voxels) - -link_shared_dependencies() \ No newline at end of file diff --git a/voxel-edit/src/SceneUtils.cpp b/voxel-edit/src/SceneUtils.cpp deleted file mode 100644 index 937b03cc39..0000000000 --- a/voxel-edit/src/SceneUtils.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// -// SceneUtils.cpp -// voxel-edit/src -// -// Created by Brad Hefta-Gaub on 5/7/2013. -// Copyright 2013 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 - -#include - -#include "SceneUtils.h" - -void addCornersAndAxisLines(VoxelTree* tree) { - // We want our corner voxels to be about 1/2 meter high, and our TREE_SCALE is in meters, so... - float voxelSize = 0.5f / TREE_SCALE; - - // Now some more examples... a little more complex - qDebug("creating corner points..."); - tree->createVoxel(0 , 0 , 0 , voxelSize, 255, 255 ,255); - tree->createVoxel(1.0 - voxelSize, 0 , 0 , voxelSize, 255, 0 ,0 ); - tree->createVoxel(0 , 1.0 - voxelSize, 0 , voxelSize, 0 , 255 ,0 ); - tree->createVoxel(0 , 0 , 1.0 - voxelSize, voxelSize, 0 , 0 ,255); - tree->createVoxel(1.0 - voxelSize, 0 , 1.0 - voxelSize, voxelSize, 255, 0 ,255); - tree->createVoxel(0 , 1.0 - voxelSize, 1.0 - voxelSize, voxelSize, 0 , 255 ,255); - tree->createVoxel(1.0 - voxelSize, 1.0 - voxelSize, 0 , voxelSize, 255, 255 ,0 ); - tree->createVoxel(1.0 - voxelSize, 1.0 - voxelSize, 1.0 - voxelSize, voxelSize, 255, 255 ,255); - qDebug("DONE creating corner points..."); -} - -void addSurfaceScene(VoxelTree * tree) { - qDebug("adding surface scene..."); - float voxelSize = 1.f / (8 * TREE_SCALE); - - // color 1= blue, color 2=green - unsigned char r1, g1, b1, r2, g2, b2, red, green, blue; - r1 = r2 = b2 = g1 = 0; - b1 = g2 = 255; - - for (float x = 0.0; x < 1.0; x += voxelSize) { - for (float z = 0.0; z < 1.0; z += voxelSize) { - - glm::vec2 position = glm::vec2(x, z); - float perlin = glm::perlin(position) + .25f * glm::perlin(position * 4.f) + .125f * glm::perlin(position * 16.f); - float gradient = (1.0f + perlin)/ 2.0f; - red = (unsigned char)std::min(255, std::max(0, (int)(r1 + ((r2 - r1) * gradient)))); - green = (unsigned char)std::min(255, std::max(0, (int)(g1 + ((g2 - g1) * gradient)))); - blue = (unsigned char)std::min(255, std::max(0, (int)(b1 + ((b2 - b1) * gradient)))); - - int height = (4 * gradient)+1; // make it at least 4 thick, so we get some averaging - for (int i = 0; i < height; i++) { - tree->createVoxel(x, ((i+1) * voxelSize) , z, voxelSize, red, green, blue); - } - } - } - qDebug("DONE adding surface scene..."); -} diff --git a/voxel-edit/src/SceneUtils.h b/voxel-edit/src/SceneUtils.h deleted file mode 100644 index 26c8130c4b..0000000000 --- a/voxel-edit/src/SceneUtils.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// SceneUtils.h -// voxel-edit/src -// -// Created by Brad Hefta-Gaub on 5/7/2013. -// Copyright 2013 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_SceneUtils_h -#define hifi_SceneUtils_h - -#include "VoxelTree.h" -#include - -void addCornersAndAxisLines(VoxelTree* tree); -void addSurfaceScene(VoxelTree * tree); - - -#endif // hifi_SceneUtils_h diff --git a/voxel-edit/src/main.cpp b/voxel-edit/src/main.cpp deleted file mode 100644 index 01e4cc0189..0000000000 --- a/voxel-edit/src/main.cpp +++ /dev/null @@ -1,482 +0,0 @@ -// -// main.cpp -// voxel-edit/src -// -// Created by Brad Hefta-Gaub on 05/03/13. -// Copyright 2013 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 -#include -#include "SceneUtils.h" -#include -#include -#include - - -int _nodeCount=0; -bool countVoxelsOperation(VoxelTreeElement* node, void* extraData) { - if (node->isColored()){ - _nodeCount++; - } - return true; // keep going -} - -void addLandscape(VoxelTree * tree) { - qDebug("Adding Landscape..."); -} - -void voxelTutorial(VoxelTree * tree) { - qDebug("adding scene..."); - - // We want our corner voxels to be about 1/2 meter high, and our TREE_SCALE is in meters, so... - float voxelSize = 0.5f / TREE_SCALE; - - // Here's an example of how to create a voxel. - qDebug("creating corner points..."); - tree->createVoxel(0, 0, 0, voxelSize, 255, 255 ,255); - - // Here's an example of how to test if a voxel exists - VoxelTreeElement* node = tree->getVoxelAt(0, 0, 0, voxelSize); - if (node) { - // and how to access it's color - qDebug("corner point 0,0,0 exists... color is (%d,%d,%d)", - node->getColor()[0], node->getColor()[1], node->getColor()[2]); - } - - // here's an example of how to delete a voxel - qDebug("attempting to delete corner point 0,0,0"); - tree->deleteVoxelAt(0, 0, 0, voxelSize); - - // Test to see that the delete worked... it should be FALSE... - if (tree->getVoxelAt(0, 0, 0, voxelSize)) { - qDebug("corner point 0,0,0 exists..."); - } else { - qDebug("corner point 0,0,0 does not exists..."); - } -} - - -void processSplitSVOFile(const char* splitSVOFile,const char* splitJurisdictionRoot,const char* splitJurisdictionEndNodes) { - char outputFileName[512]; - - qDebug("splitSVOFile: %s Jurisdictions Root: %s EndNodes: %s", - splitSVOFile, splitJurisdictionRoot, splitJurisdictionEndNodes); - - VoxelTree rootSVO; - - rootSVO.readFromSVOFile(splitSVOFile); - JurisdictionMap jurisdiction(splitJurisdictionRoot, splitJurisdictionEndNodes); - - qDebug("Jurisdiction Root Octcode: "); - printOctalCode(jurisdiction.getRootOctalCode()); - - qDebug("Jurisdiction End Nodes: %d ", jurisdiction.getEndNodeCount()); - for (int i = 0; i < jurisdiction.getEndNodeCount(); i++) { - unsigned char* endNodeCode = jurisdiction.getEndNodeOctalCode(i); - qDebug("End Node: %d ", i); - printOctalCode(endNodeCode); - - // get the endNode details - VoxelPositionSize endNodeDetails; - voxelDetailsForCode(endNodeCode, endNodeDetails); - - // Now, create a split SVO for the EndNode. - // copy the EndNode into a temporary tree - VoxelTree endNodeTree; - - // create a small voxels at corners of the endNode Tree, this will is a hack - // to work around a bug in voxel server that will send Voxel not exists - // for regions that don't contain anything even if they're not in the - // jurisdiction of the server - // This hack assumes the end nodes for demo dinner since it only guarantees - // nodes in the 8 child voxels of the main root voxel - const float verySmall = 0.015625; - endNodeTree.createVoxel(0.0, 0.0, 0.0, verySmall, 1, 1, 1, true); - endNodeTree.createVoxel(1.0, 0.0, 0.0, verySmall, 1, 1, 1, true); - endNodeTree.createVoxel(0.0, 1.0, 0.0, verySmall, 1, 1, 1, true); - endNodeTree.createVoxel(0.0, 0.0, 1.0, verySmall, 1, 1, 1, true); - endNodeTree.createVoxel(1.0, 1.0, 1.0, verySmall, 1, 1, 1, true); - endNodeTree.createVoxel(1.0, 1.0, 0.0, verySmall, 1, 1, 1, true); - endNodeTree.createVoxel(0.0, 1.0, 1.0, verySmall, 1, 1, 1, true); - endNodeTree.createVoxel(1.0, 0.0, 1.0, verySmall, 1, 1, 1, true); - - // Delete the voxel for the EndNode from the temporary tree, so we can - // import our endNode content into it... - endNodeTree.deleteOctalCodeFromTree(endNodeCode, COLLAPSE_EMPTY_TREE); - - VoxelTreeElement* endNode = rootSVO.getVoxelAt(endNodeDetails.x, - endNodeDetails.y, - endNodeDetails.z, - endNodeDetails.s); - - rootSVO.copySubTreeIntoNewTree(endNode, &endNodeTree, false); - - sprintf(outputFileName, "splitENDNODE%d%s", i, splitSVOFile); - qDebug("outputFile: %s", outputFileName); - endNodeTree.writeToSVOFile(outputFileName); - - // Delete the voxel for the EndNode from the root tree... - rootSVO.deleteOctalCodeFromTree(endNodeCode, COLLAPSE_EMPTY_TREE); - - // create a small voxel in center of each EndNode, this will is a hack - // to work around a bug in voxel server that will send Voxel not exists - // for regions that don't contain anything even if they're not in the - // jurisdiction of the server - float x = endNodeDetails.x + endNodeDetails.s * 0.5; - float y = endNodeDetails.y + endNodeDetails.s * 0.5; - float z = endNodeDetails.z + endNodeDetails.s * 0.5; - float s = endNodeDetails.s * verySmall; - - rootSVO.createVoxel(x, y, z, s, 1, 1, 1, true); - - } - - sprintf(outputFileName, "splitROOT%s", splitSVOFile); - qDebug("outputFile: %s", outputFileName); - rootSVO.writeToSVOFile(outputFileName); - - qDebug("exiting now"); -} - -class copyAndFillArgs { -public: - VoxelTree* destinationTree; - unsigned long outCount; - unsigned long inCount; - unsigned long originalCount; - -}; - -bool copyAndFillOperation(OctreeElement* element, void* extraData) { - VoxelTreeElement* voxel = (VoxelTreeElement*)element; - copyAndFillArgs* args = (copyAndFillArgs*)extraData; - char outputMessage[128]; - - args->inCount++; - int percentDone = (100*args->inCount/args->originalCount); - - // For each leaf node... - if (voxel->isLeaf()) { - // create a copy of the leaf in the copy destination - float x = voxel->getCorner().x; - float y = voxel->getCorner().y; - float z = voxel->getCorner().z; - float s = voxel->getScale(); - unsigned char red = voxel->getColor()[RED_INDEX]; - unsigned char green = voxel->getColor()[GREEN_INDEX]; - unsigned char blue = voxel->getColor()[BLUE_INDEX]; - bool destructive = true; - - args->destinationTree->createVoxel(x, y, z, s, red, green, blue, destructive); - args->outCount++; - - sprintf(outputMessage,"Completed: %d%% (%lu of %lu) - Creating voxel %lu at [%f,%f,%f,%f]", - percentDone,args->inCount,args->originalCount,args->outCount,x,y,z,s); - printf("%s",outputMessage); - for (unsigned int b = 0; b < strlen(outputMessage); b++) { - printf("\b"); - } - - // and create same sized leafs from this leaf voxel down to zero in the destination tree - for (float yFill = y-s; yFill >= 0.0f; yFill -= s) { - args->destinationTree->createVoxel(x, yFill, z, s, red, green, blue, destructive); - - args->outCount++; - - sprintf(outputMessage,"Completed: %d%% (%lu of %lu) - Creating fill voxel %lu at [%f,%f,%f,%f]", - percentDone,args->inCount,args->originalCount,args->outCount,x,y,z,s); - printf("%s",outputMessage); - for (unsigned int b = 0; b < strlen(outputMessage); b++) { - printf("\b"); - } - } - } - return true; -} - -void processFillSVOFile(const char* fillSVOFile) { - char outputFileName[512]; - - qDebug("fillSVOFile: %s", fillSVOFile); - - VoxelTree originalSVO(true); // reaveraging - VoxelTree filledSVO(true); // reaveraging - - originalSVO.readFromSVOFile(fillSVOFile); - qDebug("Nodes after loading %lu nodes", originalSVO.getOctreeElementsCount()); - originalSVO.reaverageOctreeElements(); - qDebug("Original Voxels reAveraged"); - qDebug("Nodes after reaveraging %lu nodes", originalSVO.getOctreeElementsCount()); - - copyAndFillArgs args; - args.destinationTree = &filledSVO; - args.inCount = 0; - args.outCount = 0; - args.originalCount = originalSVO.getOctreeElementsCount(); - qDebug("Begin processing..."); - originalSVO.recurseTreeWithOperation(copyAndFillOperation, &args); - qDebug("DONE processing..."); - - qDebug("Original input nodes used for filling %lu nodes", args.originalCount); - qDebug("Input nodes traversed during filling %lu nodes", args.inCount); - qDebug("Nodes created during filling %lu nodes", args.outCount); - qDebug("Nodes after filling %lu nodes", filledSVO.getOctreeElementsCount()); - - filledSVO.reaverageOctreeElements(); - qDebug("Nodes after reaveraging %lu nodes", filledSVO.getOctreeElementsCount()); - - sprintf(outputFileName, "filled%s", fillSVOFile); - qDebug("outputFile: %s", outputFileName); - filledSVO.writeToSVOFile(outputFileName); - - qDebug("exiting now"); -} - -void unitTest(VoxelTree * tree); - - -int main(int argc, const char * argv[]) -{ - VoxelTree myTree; - - qInstallMessageHandler(sharedMessageHandler); - - unitTest(&myTree); - - - const char* GET_OCTCODE = "--getOctCode"; - const char* octcodeParams = getCmdOption(argc, argv, GET_OCTCODE); - if (octcodeParams) { - - QString octcodeParamsString(octcodeParams); - QStringList octcodeParamsList = octcodeParamsString.split(QString(",")); - - enum { X_AT, Y_AT, Z_AT, S_AT, EXPECTED_PARAMS }; - if (octcodeParamsList.size() == EXPECTED_PARAMS) { - QString xStr = octcodeParamsList.at(X_AT); - QString yStr = octcodeParamsList.at(Y_AT); - QString zStr = octcodeParamsList.at(Z_AT); - QString sStr = octcodeParamsList.at(S_AT); - - float x = xStr.toFloat()/TREE_SCALE; // 0.14745788574219; - float y = yStr.toFloat()/TREE_SCALE; // 0.01502178955078; - float z = zStr.toFloat()/TREE_SCALE; // 0.56540045166016; - float s = sStr.toFloat()/TREE_SCALE; // 0.015625; - - qDebug() << "Get Octal Code for:"; - qDebug() << " x:" << xStr << " [" << x << "]"; - qDebug() << " y:" << yStr << " [" << y << "]"; - qDebug() << " z:" << zStr << " [" << z << "]"; - qDebug() << " s:" << sStr << " [" << s << "]"; - - unsigned char* octalCode = pointToVoxel(x, y, z, s); - QString octalCodeStr = octalCodeToHexString(octalCode); - qDebug() << "octal code: " << octalCodeStr; - - } else { - qDebug() << "Unexpected number of parameters for getOctCode"; - } - return 0; - } - - const char* DECODE_OCTCODE = "--decodeOctCode"; - const char* decodeParam = getCmdOption(argc, argv, DECODE_OCTCODE); - if (decodeParam) { - - QString decodeParamsString(decodeParam); - unsigned char* octalCodeToDecode = hexStringToOctalCode(decodeParamsString); - - VoxelPositionSize details; - voxelDetailsForCode(octalCodeToDecode, details); - - delete[] octalCodeToDecode; - - qDebug() << "octal code to decode: " << decodeParamsString; - qDebug() << "Details for Octal Code:"; - qDebug() << " x:" << details.x << "[" << details.x * TREE_SCALE << "]"; - qDebug() << " y:" << details.y << "[" << details.y * TREE_SCALE << "]"; - qDebug() << " z:" << details.z << "[" << details.z * TREE_SCALE << "]"; - qDebug() << " s:" << details.s << "[" << details.s * TREE_SCALE << "]"; - return 0; - } - - - // Handles taking and SVO and splitting it into multiple SVOs based on - // jurisdiction details - const char* SPLIT_SVO = "--splitSVO"; - const char* splitSVOFile = getCmdOption(argc, argv, SPLIT_SVO); - const char* SPLIT_JURISDICTION_ROOT = "--splitJurisdictionRoot"; - const char* SPLIT_JURISDICTION_ENDNODES = "--splitJurisdictionEndNodes"; - const char* splitJurisdictionRoot = getCmdOption(argc, argv, SPLIT_JURISDICTION_ROOT); - const char* splitJurisdictionEndNodes = getCmdOption(argc, argv, SPLIT_JURISDICTION_ENDNODES); - if (splitSVOFile && splitJurisdictionRoot && splitJurisdictionEndNodes) { - processSplitSVOFile(splitSVOFile, splitJurisdictionRoot, splitJurisdictionEndNodes); - return 0; - } - - - // Handles taking an SVO and filling in the empty space below the voxels to make it solid. - const char* FILL_SVO = "--fillSVO"; - const char* fillSVOFile = getCmdOption(argc, argv, FILL_SVO); - if (fillSVOFile) { - processFillSVOFile(fillSVOFile); - return 0; - } - - const char* DONT_CREATE_FILE = "--dontCreateSceneFile"; - bool dontCreateFile = cmdOptionExists(argc, argv, DONT_CREATE_FILE); - - if (dontCreateFile) { - qDebug("You asked us not to create a scene file, so we will not."); - } else { - qDebug("Creating Scene File..."); - - const char* RUN_TUTORIAL = "--runTutorial"; - if (cmdOptionExists(argc, argv, RUN_TUTORIAL)) { - voxelTutorial(&myTree); - } - - const char* ADD_CORNERS_AND_AXIS_LINES = "--addCornersAndAxisLines"; - if (cmdOptionExists(argc, argv, ADD_CORNERS_AND_AXIS_LINES)) { - addCornersAndAxisLines(&myTree); - } - - const char* ADD_SURFACE_SCENE = "--addSurfaceScene"; - if (cmdOptionExists(argc, argv, ADD_SURFACE_SCENE)) { - addSurfaceScene(&myTree); - } - - unsigned long nodeCount = myTree.getOctreeElementsCount(); - qDebug("Nodes after adding scenes: %ld nodes", nodeCount); - - myTree.writeToSVOFile("voxels.svo"); - } - return 0; -} - -void unitTest(VoxelTree * tree) { - VoxelTreeElement* node = NULL; - qDebug("unit tests..."); - unsigned long nodeCount; - - // We want our corner voxels to be about 1/2 meter high, and our TREE_SCALE is in meters, so... - float voxelSize = 0.5f / TREE_SCALE; - - // Here's an example of how to create a voxel. - qDebug("creating corner points..."); - tree->createVoxel(0, 0, 0, voxelSize, 255, 255 ,255); - qDebug("Nodes at line %d... %ld nodes", __LINE__, tree->getOctreeElementsCount()); - - - // Here's an example of how to test if a voxel exists - node = tree->getVoxelAt(0, 0, 0, voxelSize); - if (node) { - // and how to access it's color - qDebug("CORRECT - corner point 0,0,0 exists... color is (%d,%d,%d) ", - node->getColor()[0], node->getColor()[1], node->getColor()[2]); - } - - qDebug("Nodes at line %d... %ld nodes", __LINE__, tree->getOctreeElementsCount()); - - // here's an example of how to delete a voxel - qDebug("attempting to delete corner point 0,0,0"); - tree->deleteVoxelAt(0, 0, 0, voxelSize); - - qDebug("Nodes at line %d... %ld nodes", __LINE__, tree->getOctreeElementsCount()); - - // Test to see that the delete worked... it should be FALSE... - if ((node = tree->getVoxelAt(0, 0, 0, voxelSize))) { - qDebug("FAIL corner point 0,0,0 exists..."); - } else { - qDebug("CORRECT corner point 0,0,0 does not exists..."); - } - - qDebug("Nodes at line %d... %ld nodes", __LINE__, tree->getOctreeElementsCount()); - - tree->createVoxel(0, 0, 0, voxelSize, 255, 255 ,255); - if ((node = tree->getVoxelAt(0, 0, 0, voxelSize))) { - qDebug("CORRECT - corner point 0,0,0 exists... color is (%d,%d,%d) ", - node->getColor()[0], node->getColor()[1], node->getColor()[2]); - } else { - qDebug("FAIL corner point 0,0,0 does not exists..."); - } - - qDebug("Nodes at line %d... %ld nodes", __LINE__, tree->getOctreeElementsCount()); - - tree->createVoxel(voxelSize, 0, 0, voxelSize, 255, 255 ,0); - if ((node = tree->getVoxelAt(voxelSize, 0, 0, voxelSize))) { - qDebug("CORRECT - corner point voxelSize,0,0 exists... color is (%d,%d,%d) ", - node->getColor()[0], node->getColor()[1], node->getColor()[2]); - } else { - qDebug("FAIL corner point voxelSize,0,0 does not exists..."); - } - - qDebug("Nodes at line %d... %ld nodes", __LINE__, tree->getOctreeElementsCount()); - - tree->createVoxel(0, 0, voxelSize, voxelSize, 255, 0 ,0); - if ((node = tree->getVoxelAt(0, 0, voxelSize, voxelSize))) { - qDebug("CORRECT - corner point 0, 0, voxelSize exists... color is (%d,%d,%d) ", - node->getColor()[0], node->getColor()[1], node->getColor()[2]); - } else { - qDebug("FAILED corner point 0, 0, voxelSize does not exists..."); - } - - qDebug("Nodes at line %d... %ld nodes", __LINE__, tree->getOctreeElementsCount()); - - tree->createVoxel(voxelSize, 0, voxelSize, voxelSize, 0, 0 ,255); - if ((node = tree->getVoxelAt(voxelSize, 0, voxelSize, voxelSize))) { - qDebug("CORRECT - corner point voxelSize, 0, voxelSize exists... color is (%d,%d,%d) ", - node->getColor()[0], node->getColor()[1], node->getColor()[2]); - } else { - qDebug("corner point voxelSize, 0, voxelSize does not exists..."); - } - - qDebug("Nodes at line %d... %ld nodes", __LINE__, tree->getOctreeElementsCount()); - - qDebug("check root voxel exists..."); - if (tree->getVoxelAt(0,0,0,1.0)) { - qDebug("of course it does"); - } else { - qDebug("WTH!?!"); - } - - nodeCount = tree->getOctreeElementsCount(); - qDebug("Nodes before writing file: %ld nodes", nodeCount); - - tree->writeToSVOFile("voxels.svo"); - - qDebug("erasing the tree..."); - tree->eraseAllOctreeElements(); - - qDebug("check root voxel exists..."); - if (tree->getVoxelAt(0,0,0,1.0)) { - qDebug("of course it does"); - } else { - qDebug("WTH!?!"); - } - - // this should not exist... we just deleted it... - if (tree->getVoxelAt(voxelSize, 0, voxelSize, voxelSize)) { - qDebug("corner point voxelSize, 0, voxelSize exists..."); - } else { - qDebug("corner point voxelSize, 0, voxelSize does not exists..."); - } - - tree->readFromSVOFile("voxels.svo"); - - // this should exist... we just loaded it... - if (tree->getVoxelAt(voxelSize, 0, voxelSize, voxelSize)) { - qDebug("corner point voxelSize, 0, voxelSize exists..."); - } else { - qDebug("corner point voxelSize, 0, voxelSize does not exists..."); - } - - nodeCount = tree->getOctreeElementsCount(); - qDebug("Nodes after loading file: %ld nodes", nodeCount); - - -}