mirror of
https://github.com/overte-org/overte.git
synced 2025-04-16 16:26:17 +02:00
merge upstream/master into andrew/inertia
Conflicts: interface/src/Application.cpp
This commit is contained in:
commit
37093cbd00
116 changed files with 5604 additions and 6907 deletions
|
@ -67,4 +67,3 @@ add_subdirectory(ice-server)
|
|||
add_subdirectory(interface)
|
||||
add_subdirectory(tests)
|
||||
add_subdirectory(tools)
|
||||
add_subdirectory(voxel-edit)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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.";
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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];
|
||||
|
|
93
examples/entityScripts/inspect.js
Normal file
93
examples/entityScripts/inspect.js
Normal 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);
|
||||
}
|
||||
};
|
||||
|
||||
})
|
372
examples/entityScripts/sitOnEntity.js
Normal file
372
examples/entityScripts/sitOnEntity.js
Normal 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
|
||||
};
|
||||
|
||||
})
|
|
@ -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
|
||||
|
|
|
@ -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
27
examples/hotPlaces.js
Normal 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";
|
||||
}
|
||||
});
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
377
examples/notifications.js
Normal 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);
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "DatagramSequencer.h"
|
||||
#include "MetavoxelData.h"
|
||||
#include "Spanner.h"
|
||||
|
||||
class PacketRecord;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
2126
libraries/metavoxels/src/Spanner.cpp
Normal file
2126
libraries/metavoxels/src/Spanner.cpp
Normal file
File diff suppressed because it is too large
Load diff
536
libraries/metavoxels/src/Spanner.h
Normal file
536
libraries/metavoxels/src/Spanner.h
Normal 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
|
|
@ -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('/')) {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
//
|
||||
|
|
|
@ -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.
|
||||
//
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue