merge upstream/master into andrew/inertia

Conflicts:
	interface/src/Application.cpp
This commit is contained in:
Andrew Meadows 2014-11-24 11:24:39 -08:00
commit 37093cbd00
116 changed files with 5604 additions and 6907 deletions

View file

@ -67,4 +67,3 @@ add_subdirectory(ice-server)
add_subdirectory(interface)
add_subdirectory(tests)
add_subdirectory(tools)
add_subdirectory(voxel-edit)

View file

@ -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;

View file

@ -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());

View file

@ -9,7 +9,9 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QCoreApplication>
#include <QDateTime>
#include <QDir>
#include <QFile>
#include <QSaveFile>
#include <QThread>
@ -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.";

View file

@ -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();

View file

@ -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";

View file

@ -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

View file

@ -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++) {

View file

@ -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];

View file

@ -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);
}
};
})

View file

@ -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
};
})

View file

@ -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

View file

@ -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;
}

27
examples/hotPlaces.js Normal file
View file

@ -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";
}
});

View file

@ -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) {

View file

@ -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) {

View file

@ -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);
Script.scriptEnding.connect(maybeCleanupLobby);
Controller.mouseMoveEvent.connect(mouseMoveEvent);

View file

@ -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",

377
examples/notifications.js Normal file
View file

@ -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);

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;
}
}

View file

@ -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) {

View file

@ -26,8 +26,6 @@
#include "AudioSourceTone.h"
#include "AudioSourceNoise.h"
#include "AudioGain.h"
#include "AudioFilter.h"
#include "AudioFilterBank.h"
#include <QAudio>
#include <QAudioInput>
@ -47,10 +45,17 @@
#include <AudioRingBuffer.h>
#include <StDev.h>
#ifdef _WIN32
#pragma warning( push )
#pragma warning( disable : 4305 )
#endif
extern "C" {
#include <gverb.h>
#include <gverbdsp.h>
}
#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<QObject*, QAudioOutput*> _injectedOutputInterfaces;
};

View file

@ -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);

View file

@ -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);

View file

@ -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();

View file

@ -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);

View file

@ -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,

View file

@ -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";

File diff suppressed because it is too large Load diff

View file

@ -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<HeightfieldBaseLayerBatch> _heightfieldBaseBatches;
QVector<HeightfieldSplatBatch> _heightfieldSplatBatches;
QVector<VoxelBatch> _voxelBaseBatches;
QVector<VoxelSplatBatch> _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<BufferPoint> 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<BufferData> 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<SharedObjectPointer>& materials = QVector<SharedObjectPointer>());
~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<SharedObjectPointer>& getMaterials() { return _materials; }
const QVector<SharedObjectPointer>& 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<SharedObjectPointer> _materials;
GLuint _heightTextureID;
GLuint _colorTextureID;
GLuint _materialTextureID;
QVector<NetworkTexturePointer> _networkTextures;
int _heightSize;
float _heightIncrement;
int _colorSize;
float _colorIncrement;
int _materialSize;
float _materialIncrement;
typedef QPair<QOpenGLBuffer, QOpenGLBuffer> BufferPair;
static QHash<int, BufferPair> _bufferPairs;
};
/// Convenience class for rendering a preview of a heightfield.
class HeightfieldPreview {
public:
void setBuffers(const QVector<BufferDataPointer>& buffers) { _buffers = buffers; }
const QVector<BufferDataPointer>& getBuffers() const { return _buffers; }
void render(const glm::vec3& translation, float scale) const;
private:
QVector<BufferDataPointer> _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<VoxelPoint>& vertices, const QVector<int>& indices, const QVector<glm::vec3>& hermite,
const QMultiHash<QRgb, int>& quadIndices, int size, const QVector<SharedObjectPointer>& materials =
const QMultiHash<VoxelCoord, int>& quadIndices, int size, const QVector<SharedObjectPointer>& materials =
QVector<SharedObjectPointer>());
/// Finds the first intersection between the described ray and the voxel data.
@ -336,7 +334,7 @@ private:
QVector<VoxelPoint> _vertices;
QVector<int> _indices;
QVector<glm::vec3> _hermite;
QMultiHash<QRgb, int> _quadIndices;
QMultiHash<VoxelCoord, int> _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<NetworkTexturePointer> _networkTextures;
typedef QPair<int, int> IntPair;
typedef QPair<QOpenGLBuffer, QOpenGLBuffer> BufferPair;
static QHash<IntPair, BufferPair> _bufferPairs;
};
#endif // hifi_MetavoxelSystem_h

View file

@ -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);
}
}

View file

@ -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<float>(x), static_cast<float>(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();
}

View file

@ -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

View file

@ -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; }

View file

@ -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();
}
}

View file

@ -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<Avatar*>(_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();

View file

@ -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<Avatar*>(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;

View file

@ -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; }

View file

@ -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<JointState> 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;

View file

@ -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);

View file

@ -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++) {

View file

@ -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 {

View file

@ -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

View file

@ -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);
}

View file

@ -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);

View file

@ -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<EntityTree*>(_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<EntityTree*>(_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];

View file

@ -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<Model*> _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);

View file

@ -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 {

View file

@ -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));

View file

@ -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);

View file

@ -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<int>& 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) {

View file

@ -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;

View file

@ -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);
};

View file

@ -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

View file

@ -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);

View file

@ -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<Spanner*>(getSpanner(true).data());
Transformable* transformable = qobject_cast<Transformable*>(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<Spanner*>(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<float> 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>& directionImages);
virtual void run();
private:
void voxelize(const glm::vec3& center);
float _size;
Box _bounds;
float _granularity;
QVector<DirectionImages> _directionImages;
};
Voxelizer::Voxelizer(float size, const Box& bounds, float granularity, const QVector<DirectionImages>& 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>& directionImages, const glm::vec3& center, float granularity);
virtual int visit(MetavoxelInfo& info);
private:
QVector<DirectionImages> _directionImages;
glm::vec3 _center;
float _granularity;
};
VoxelizationVisitor::VoxelizationVisitor(const QVector<DirectionImages>& directionImages,
const glm::vec3& center, float granularity) :
MetavoxelVisitor(QVector<AttributePointer>(), QVector<AttributePointer>() <<
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<QRgb>(color));
return STOP_RECURSION;
}
if (distance < closestDistance) {
closestColor = color;
closestDistance = distance;
}
}
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<QRgb>(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*>(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> 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<float>(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<void (QSpinBox::*)(int)>(&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<void (QDoubleSpinBox::*)(double)>(&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<void (QDoubleSpinBox::*)(double)>(&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<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this,
&ImportHeightfieldTool::updateSpanner);
}
void ImportHeightfieldTool::apply() {
float scale = _translation->getSingleStep();
foreach (const BufferDataPointer& bufferData, _preview.getBuffers()) {
HeightfieldBuffer* buffer = static_cast<HeightfieldBuffer*>(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<quint16>::max();
int maxHeight = numeric_limits<quint16>::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<BufferDataPointer> 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<Heightfield*>(_spanner.data())->getRenderer()->simulate(deltaTime);
}
void ImportHeightfieldTool::renderPreview() {
if (isVisible()) {
_preview.render(_translation->getValue(), _translation->getSingleStep());
static_cast<Heightfield*>(_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<quint16>& 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<quint16> 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<SharedObjectPointer>())));
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<Heightfield*>(_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() {

View file

@ -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<quint16> _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.

View file

@ -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());

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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);

View file

@ -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<float>(), 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);

View file

@ -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),

View file

@ -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; }

View file

@ -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),

View file

@ -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); }

View file

@ -20,12 +20,12 @@
#include <glm/gtc/quaternion.hpp>
// 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;

View file

@ -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);

View file

@ -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<EntityItem*>(_entityTree->findEntityByEntityItemID(entityID));
_entityTree->unlock();
if (entity) {
return entity->getEntityItemID();
}
return entityID;
}
EntityItemID EntityScriptingInterface::identifyEntity(EntityItemID entityID) {
EntityItemID actualID = entityID;

View file

@ -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);

View file

@ -623,7 +623,7 @@ void EntityTreeElement::getEntities(const AACube& box, QVector<EntityItem*>& 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);
}

View file

@ -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) {

File diff suppressed because it is too large Load diff

View file

@ -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<class T, int bits> inline bool SimpleInlineAttribute<T, bits>::merge(
return allChildrenEqual;
}
/// Simple float attribute.
/// A simple float attribute.
class FloatAttribute : public SimpleInlineAttribute<float> {
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<QRgb> {
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<DataBlock> DataBlockPointer;
/// Base class for blocks of data.
@ -494,124 +400,6 @@ protected:
QMutex _encodedSubdivisionsMutex;
};
typedef QExplicitlySharedDataPointer<HeightfieldHeightData> 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<HeightfieldHeightDataPointer> {
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<HeightfieldColorData> 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<HeightfieldColorDataPointer> {
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<HeightfieldMaterialData> HeightfieldMaterialDataPointer;
/// Contains a block of heightfield material data.
class HeightfieldMaterialData : public DataBlock {
public:
HeightfieldMaterialData(const QByteArray& contents,
const QVector<SharedObjectPointer>& materials = QVector<SharedObjectPointer>());
HeightfieldMaterialData(Bitstream& in, int bytes);
HeightfieldMaterialData(Bitstream& in, int bytes, const HeightfieldMaterialDataPointer& reference);
const QByteArray& getContents() const { return _contents; }
const QVector<SharedObjectPointer>& 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<SharedObjectPointer> _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<HeightfieldMaterialDataPointer> {
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<SharedObjectPointer>& materials, QByteArray& contents);

View file

@ -16,6 +16,7 @@
#include "DatagramSequencer.h"
#include "MetavoxelData.h"
#include "Spanner.h"
class PacketRecord;

View file

@ -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<AttributePointer>() <<
AttributeRegistry::getInstance()->getSpannersAttribute(),
QVector<AttributePointer>(), QVector<AttributePointer>(), 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<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute(),
QVector<AttributePointer>(), QVector<AttributePointer>(), 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) {

View file

@ -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);

File diff suppressed because it is too large Load diff

View file

@ -12,13 +12,10 @@
#ifndef hifi_MetavoxelData_h
#define hifi_MetavoxelData_h
#include <QBitArray>
#include <QHash>
#include <QMutex>
#include <QSharedData>
#include <QSharedPointer>
#include <QScriptString>
#include <QScriptValue>
#include <QVector>
#include <glm/glm.hpp>
@ -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<SharedObjectPointer>& 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<AttributePointer>& spannerInputs,
const QVector<AttributePointer>& spannerMasks = QVector<AttributePointer>(),
const QVector<AttributePointer>& inputs = QVector<AttributePointer>(),
const QVector<AttributePointer>& outputs = QVector<AttributePointer>(),
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<AttributePointer>& spannerInputs,
const QVector<AttributePointer>& spannerMasks = QVector<AttributePointer>(),
const QVector<AttributePointer>& inputs = QVector<AttributePointer>(),
const QVector<AttributePointer>& outputs = QVector<AttributePointer>(),
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<NetworkValue> _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<AttributePointer>& getAttributes() const;
/// Returns a reference to the list of corresponding attributes that we voxelize the spanner into.
virtual const QVector<AttributePointer>& 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<SharedObjectPointer>& getMaterials();
/// Finds the intersection, if any, between the specified line segment and the spanner.
virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal);
signals:
void boundsWillChange();
void boundsChanged(const Box& bounds);
protected:
SpannerRenderer* _renderer;
/// Returns the name of the class to instantiate in order to render this spanner.
virtual QByteArray getRendererClassName() const;
private:
Box _bounds;
float _placementGranularity;
float _voxelizationGranularity;
bool _masked;
QHash<QThread*, int> _lastVisits; ///< last visit identifiers for each thread
QMutex _lastVisitsMutex;
static QAtomicInt _nextVisit; ///< the global visit counter
};
/// Base class for objects that can render spanners.
class SpannerRenderer : public QObject {
Q_OBJECT
public:
enum Mode { DEFAULT_MODE, DIFFUSE_MODE, NORMAL_MODE };
Q_INVOKABLE SpannerRenderer();
virtual void init(Spanner* spanner);
virtual void simulate(float deltaTime);
virtual void render(const glm::vec4& color, Mode mode, 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<AttributePointer>& getAttributes() const;
virtual const QVector<AttributePointer>& 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<SharedObjectPointer>& materials);
QByteArray& getHeight() { return _height; }
QByteArray& getColor() { return _color; }
QByteArray& getMaterial() { return _material; }
virtual bool hasOwnColors() const;
virtual bool hasOwnMaterials() const;
virtual QRgb getColorAt(const glm::vec3& point);
virtual int getMaterialAt(const glm::vec3& point);
virtual QVector<SharedObjectPointer>& getMaterials();
virtual bool contains(const glm::vec3& point);
virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal);
private:
float _increment;
int _width;
float _heightScale;
QByteArray _height;
QByteArray _color;
QByteArray _material;
QVector<SharedObjectPointer> _materials;
};
#endif // hifi_MetavoxelData_h

View file

@ -10,6 +10,7 @@
//
#include "MetavoxelMessages.h"
#include "Spanner.h"
void MetavoxelEditMessage::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
static_cast<const MetavoxelEdit*>(edit.data())->apply(data, objects);
@ -39,8 +40,7 @@ private:
};
BoxSetEditVisitor::BoxSetEditVisitor(const BoxSetEdit& edit) :
MetavoxelVisitor(QVector<AttributePointer>(), QVector<AttributePointer>() << edit.value.getAttribute() <<
AttributeRegistry::getInstance()->getSpannerMaskAttribute()),
MetavoxelVisitor(QVector<AttributePointer>(), QVector<AttributePointer>() << 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<float>(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<float>(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<SharedObjectPointer>& getUnmaskedSpanners() const { return _unmaskedSpanners; }
virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize);
private:
Box _bounds;
QList<SharedObjectPointer> _unmaskedSpanners;
};
GatherUnmaskedSpannersVisitor::GatherUnmaskedSpannersVisitor(const Box& bounds) :
SpannerVisitor(QVector<AttributePointer>() << 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<Spanner*>(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<AttributePointer>& attributes, Spanner* spanner);
virtual int visit(MetavoxelInfo& info);
private:
Spanner* _spanner;
float _voxelizationSize;
int _steps;
};
UpdateSpannerVisitor::UpdateSpannerVisitor(const QVector<AttributePointer>& attributes, Spanner* spanner) :
MetavoxelVisitor(QVector<AttributePointer>() << 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<SharedObjectSet>()) {
static_cast<const Spanner*>(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<Spanner*>(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<Spanner*>(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<AttributePointer>& getAttributes() const { return _attributes; }
virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize);
protected:
QSet<AttributePointer> _attributes;
};
GatherSpannerAttributesVisitor::GatherSpannerAttributesVisitor(const AttributePointer& attribute) :
SpannerVisitor(QVector<AttributePointer>() << 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<AttributePointer>& attributes, Spanner* spanner);
virtual int visit(MetavoxelInfo& info);
private:
Spanner* _spanner;
};
SetSpannerEditVisitor::SetSpannerEditVisitor(const QVector<AttributePointer>& attributes, Spanner* spanner) :
MetavoxelVisitor(attributes, QVector<AttributePointer>() << 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<float>(1.0f));
}
return STOP_RECURSION;
}
void SetSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
Spanner* spanner = static_cast<Spanner*>(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<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldAttribute(),
QVector<AttributePointer>() << 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<HeightfieldHeightDataPointer>();
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<HeightfieldHeightDataPointer>(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<SharedObjectPointer> results;
data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(),
Box(position - extents, position + extents), results);
foreach (const SharedObjectPointer& spanner, results) {
Spanner* newSpanner = static_cast<Spanner*>(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<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute() <<
AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), QVector<AttributePointer>() <<
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<HeightfieldColorDataPointer>();
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<HeightfieldColorDataPointer>(newPointer));
}
}
HeightfieldMaterialDataPointer materialPointer = info.inputValues.at(1).getInlineValue<HeightfieldMaterialDataPointer>();
if (materialPointer) {
QVector<SharedObjectPointer> 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<HeightfieldMaterialDataPointer>(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<SharedObjectPointer> results;
data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(),
Box(position - extents, position + extents), results);
foreach (const SharedObjectPointer& spanner, results) {
Spanner* newSpanner = static_cast<Spanner*>(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<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldAttribute() <<
AttributeRegistry::getInstance()->getHeightfieldColorAttribute() <<
AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), QVector<AttributePointer>() <<
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<HeightfieldHeightDataPointer>();
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<Heightfield*>(_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<SharedObjectPointer>());
}
// 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<HeightfieldHeightDataPointer>(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<HeightfieldColorDataPointer>();
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<HeightfieldColorDataPointer>(newColorPointer));
}
} else {
info.outputValues[1] = AttributeValue(_outputs.at(1));
}
}
HeightfieldMaterialDataPointer materialPointer = info.inputValues.at(2).getInlineValue<HeightfieldMaterialDataPointer>();
if (materialPointer) {
contents = materialPointer->getContents();
QVector<SharedObjectPointer> 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<int, int> 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<HeightfieldMaterialDataPointer>(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<Spanner*>(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<SharedObjectPointer> results;
data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(), expandedBounds, results);
// clear/voxelize as appropriate
SharedObjectPointer heightfield;
foreach (const SharedObjectPointer& result, results) {
Spanner* newSpanner = static_cast<Spanner*>(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<Spanner*>(heightfieldVisitor.getSpanner().data()),
material, color);
if (heightfield) {
VoxelMaterialSpannerEditVisitor visitor(static_cast<Spanner*>(heightfield.data()), material, color);
data.guide(visitor);
}

View file

@ -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

View file

@ -14,7 +14,6 @@
#include <QDoubleSpinBox>
#include <QFormLayout>
#include <QHBoxLayout>
#include <QItemEditorCreatorBase>
#include <QItemEditorFactory>
#include <QLineEdit>
#include <QMetaType>
@ -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 T> class LazyItemEditorCreator : public QItemEditorCreatorBase {
public:
virtual QWidget* createWidget(QWidget* parent) const { return new T(parent); }
virtual QByteArray valuePropertyName() const;
protected:
QByteArray _valuePropertyName;
};
template<class T> QByteArray LazyItemEditorCreator<T>::valuePropertyName() const {
if (_valuePropertyName.isNull()) {
const_cast<LazyItemEditorCreator<T>*>(this)->_valuePropertyName = T::staticMetaObject.userProperty().name();
}
return _valuePropertyName;
}
static QItemEditorCreatorBase* createDoubleEditorCreator() {
QItemEditorCreatorBase* creator = new LazyItemEditorCreator<DoubleEditor>();
getItemEditorFactory()->registerEditor(qMetaTypeId<double>(), creator);

View file

@ -14,6 +14,7 @@
#include <QColor>
#include <QComboBox>
#include <QItemEditorCreatorBase>
#include <QSharedPointer>
#include <QUrl>
#include <QWidget>
@ -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 T> class LazyItemEditorCreator : public QItemEditorCreatorBase {
public:
virtual QWidget* createWidget(QWidget* parent) const { return new T(parent); }
virtual QByteArray valuePropertyName() const;
protected:
QByteArray _valuePropertyName;
};
template<class T> QByteArray LazyItemEditorCreator<T>::valuePropertyName() const {
if (_valuePropertyName.isNull()) {
const_cast<LazyItemEditorCreator<T>*>(this)->_valuePropertyName = T::staticMetaObject.userProperty().name();
}
return _valuePropertyName;
}
/// Editor for meta-object values.
class QMetaObjectEditor : public QWidget {
Q_OBJECT

File diff suppressed because it is too large Load diff

View file

@ -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 <glm/glm.hpp>
#include "AttributeRegistry.h"
#include "MetavoxelUtil.h"
class HeightfieldColor;
class HeightfieldHeight;
class HeightfieldMaterial;
class SpannerRenderer;
/// An object that spans multiple octree cells.
class Spanner : public SharedObject {
Q_OBJECT
Q_PROPERTY(Box bounds MEMBER _bounds WRITE setBounds NOTIFY boundsChanged DESIGNABLE false)
Q_PROPERTY(float placementGranularity MEMBER _placementGranularity DESIGNABLE false)
Q_PROPERTY(float voxelizationGranularity MEMBER _voxelizationGranularity DESIGNABLE false)
public:
/// Returns the value of the global visit counter and increments it.
static int getAndIncrementNextVisit() { return _nextVisit.fetchAndAddOrdered(1); }
Spanner();
void setBounds(const Box& bounds);
const Box& getBounds() const { return _bounds; }
void setPlacementGranularity(float granularity) { _placementGranularity = granularity; }
float getPlacementGranularity() const { return _placementGranularity; }
void setVoxelizationGranularity(float granularity) { _voxelizationGranularity = granularity; }
float getVoxelizationGranularity() const { return _voxelizationGranularity; }
void setMerged(bool merged) { _merged = merged; }
bool isMerged() const { return _merged; }
/// Checks whether we've visited this object on the current traversal. If we have, returns false.
/// If we haven't, sets the last visit identifier and returns true.
bool testAndSetVisited(int visit);
/// Returns a pointer to the renderer, creating it if necessary.
SpannerRenderer* getRenderer();
/// 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<SharedObjectPointer>& getMaterials();
/// Finds the intersection, if any, between the specified line segment and the spanner.
virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal);
signals:
void boundsWillChange();
void boundsChanged(const Box& bounds);
protected:
SpannerRenderer* _renderer;
/// Returns the name of the class to instantiate in order to render this spanner.
virtual QByteArray getRendererClassName() const;
private:
Box _bounds;
float _placementGranularity;
float _voxelizationGranularity;
bool _merged;
QHash<QThread*, int> _lastVisits; ///< last visit identifiers for each thread
QMutex _lastVisitsMutex;
static QAtomicInt _nextVisit; ///< the global visit counter
};
/// Base class for objects that can render spanners.
class SpannerRenderer : public QObject {
Q_OBJECT
public:
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<HeightfieldHeight> 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<quint16>& contents);
HeightfieldHeight(Bitstream& in, int bytes);
HeightfieldHeight(Bitstream& in, int bytes, const HeightfieldHeightPointer& reference);
QVector<quint16>& getContents() { return _contents; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const HeightfieldHeightPointer& reference);
private:
void read(Bitstream& in, int bytes);
QVector<quint16> _contents;
};
Q_DECLARE_METATYPE(HeightfieldHeightPointer)
Bitstream& operator<<(Bitstream& out, const HeightfieldHeightPointer& value);
Bitstream& operator>>(Bitstream& in, HeightfieldHeightPointer& value);
template<> void Bitstream::writeRawDelta(const HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference);
template<> void Bitstream::readRawDelta(HeightfieldHeightPointer& value, const HeightfieldHeightPointer& reference);
/// 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<HeightfieldColor> HeightfieldColorPointer;
/// A block of color data associated with a heightfield.
class HeightfieldColor : public HeightfieldData {
public:
HeightfieldColor(int width, const QByteArray& contents);
HeightfieldColor(Bitstream& in, int bytes);
HeightfieldColor(Bitstream& in, int bytes, const HeightfieldColorPointer& reference);
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<HeightfieldMaterial> HeightfieldMaterialPointer;
/// A block of material data associated with a heightfield.
class HeightfieldMaterial : public HeightfieldData {
public:
HeightfieldMaterial(int width, const QByteArray& contents, const QVector<SharedObjectPointer>& materials);
HeightfieldMaterial(Bitstream& in, int bytes);
HeightfieldMaterial(Bitstream& in, int bytes, const HeightfieldMaterialPointer& reference);
QByteArray& getContents() { return _contents; }
QVector<SharedObjectPointer>& getMaterials() { return _materials; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const HeightfieldMaterialPointer& reference);
private:
void read(Bitstream& in, int bytes);
QByteArray _contents;
QVector<SharedObjectPointer> _materials;
};
Q_DECLARE_METATYPE(HeightfieldMaterialPointer)
Bitstream& operator<<(Bitstream& out, const HeightfieldMaterialPointer& value);
Bitstream& operator>>(Bitstream& in, HeightfieldMaterialPointer& value);
template<> void Bitstream::writeRawDelta(const HeightfieldMaterialPointer& value, const HeightfieldMaterialPointer& reference);
template<> void Bitstream::readRawDelta(HeightfieldMaterialPointer& value, const HeightfieldMaterialPointer& reference);
/// A heightfield represented as a spanner.
class Heightfield : public Transformable {
Q_OBJECT
Q_PROPERTY(float aspectY MEMBER _aspectY WRITE setAspectY NOTIFY aspectYChanged)
Q_PROPERTY(float aspectZ MEMBER _aspectZ WRITE setAspectZ NOTIFY aspectZChanged)
Q_PROPERTY(HeightfieldHeightPointer height MEMBER _height WRITE setHeight NOTIFY heightChanged)
Q_PROPERTY(HeightfieldColorPointer color MEMBER _color WRITE setColor NOTIFY colorChanged)
Q_PROPERTY(HeightfieldMaterialPointer material MEMBER _material WRITE setMaterial NOTIFY materialChanged 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<SharedObjectPointer>& getMaterials();
virtual bool contains(const glm::vec3& point);
virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal);
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

View file

@ -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('/')) {

View file

@ -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:

View file

@ -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.
//

View file

@ -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.
//

View file

@ -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;

View file

@ -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 {

View file

@ -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();

View file

@ -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();
}

View file

@ -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

View file

@ -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);
}

View file

@ -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);

View file

@ -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<uint16_t>::max() / 360.f);
const float ANGLE_CONVERSION_RATIO = (std::numeric_limits<uint16_t>::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<uint16_t>::max()) * 360.f - 180.f;
*destinationPointer = (*byteAnglePointer / (float) std::numeric_limits<uint16_t>::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<uint16_t>::max() / 2.f);
const float QUAT_PART_CONVERSION_RATIO = (std::numeric_limits<uint16_t>::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<uint16_t>::max()) * 2.f) - 1.f;
quatOutput.y = ((quatParts[1] / (float) std::numeric_limits<uint16_t>::max()) * 2.f) - 1.f;
quatOutput.z = ((quatParts[2] / (float) std::numeric_limits<uint16_t>::max()) * 2.f) - 1.f;
quatOutput.w = ((quatParts[3] / (float) std::numeric_limits<uint16_t>::max()) * 2.f) - 1.f;
quatOutput.x = ((quatParts[0] / (float) std::numeric_limits<uint16_t>::max()) * 2.0f) - 1.0f;
quatOutput.y = ((quatParts[1] / (float) std::numeric_limits<uint16_t>::max()) * 2.0f) - 1.0f;
quatOutput.z = ((quatParts[2] / (float) std::numeric_limits<uint16_t>::max()) * 2.0f) - 1.0f;
quatOutput.w = ((quatParts[3] / (float) std::numeric_limits<uint16_t>::max()) * 2.0f) - 1.0f;
return sizeof(quatParts);
}

Some files were not shown because too many files have changed in this diff Show more