merge upstream/master into andrew/inertia

This commit is contained in:
Andrew Meadows 2014-05-08 17:11:55 -07:00
commit d725be37d0
71 changed files with 2241 additions and 964 deletions

View file

@ -54,7 +54,7 @@
const short JITTER_BUFFER_MSECS = 12;
const short JITTER_BUFFER_SAMPLES = JITTER_BUFFER_MSECS * (SAMPLE_RATE / 1000.0);
const float LOUDNESS_TO_DISTANCE_RATIO = 0.00305f;
const float LOUDNESS_TO_DISTANCE_RATIO = 0.00001f;
const QString AUDIO_MIXER_LOGGING_TARGET_NAME = "audio-mixer";
@ -348,6 +348,16 @@ void AudioMixer::readPendingDatagrams() {
|| mixerPacketType == PacketTypeSilentAudioFrame) {
nodeList->findNodeAndUpdateWithDataFromPacket(receivedPacket);
} else if (mixerPacketType == PacketTypeMuteEnvironment) {
QByteArray packet = receivedPacket;
populatePacketHeader(packet, PacketTypeMuteEnvironment);
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
if (node->getType() == NodeType::Agent && node->getActiveSocket() && node->getLinkedData() && node != nodeList->sendingNodeForPacket(receivedPacket)) {
nodeList->writeDatagram(packet, packet.size(), node);
}
}
} else {
// let processNodeData handle it.
nodeList->processNodeData(senderSockAddr, receivedPacket);

96
examples/airGuitar.js Normal file
View file

@ -0,0 +1,96 @@
//
// airGuitar.js
// examples
//
// Copyright 2014 High Fidelity, Inc.
//
// This example musical instrument script plays guitar chords based on a strum motion and hand position
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
function length(v) {
return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
}
function printVector(v) {
print(v.x + ", " + v.y + ", " + v.z);
return;
}
function vMinus(a, b) {
var rval = { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z };
return rval;
}
// First, load two percussion sounds to be used on the sticks
var guitarType = 2;
if (guitarType == 1) {
var chord1 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Nylon+A.raw");
var chord2 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Nylon+B.raw");
var chord3 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Nylon+E.raw");
} else {
var chord1 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Metal+A+short.raw");
var chord2 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Metal+B+short.raw");
var chord3 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Metal+E+short.raw");
}
var whichChord = chord1;
var leftHanded = false;
if (leftHanded) {
var strumHand = 0;
var chordHand = 1;
} else {
var strumHand = 1;
var chordHand = 0;
}
var lastPosition = { x: 0.0,
y: 0.0,
z: 0.0 };
function checkHands(deltaTime) {
for (var palm = 0; palm < 2; palm++) {
var palmVelocity = Controller.getSpatialControlVelocity(palm * 2 + 1);
var speed = length(palmVelocity);
var position = Controller.getSpatialControlPosition(palm * 2 + 1);
var myPelvis = MyAvatar.position;
if (palm == strumHand) {
var STRUM_HEIGHT_ABOVE_PELVIS = -0.30;
var strumTriggerHeight = myPelvis.y + STRUM_HEIGHT_ABOVE_PELVIS;
//printVector(position);
if ((position.y < strumTriggerHeight) && (lastPosition.y >= strumTriggerHeight)) {
// If hand passes downward through guitar strings, play a chord!
var options = new AudioInjectionOptions();
options.position = position;
if (speed > 1.0) { speed = 1.0; }
options.volume = speed;
Audio.playSound(whichChord, options);
}
lastPosition = Controller.getSpatialControlPosition(palm * 2 + 1);
} else {
// This is the chord controller
var distanceFromPelvis = Vec3.length(Vec3.subtract(position, myPelvis));
//print(distanceFromPelvis);
if (distanceFromPelvis > 0.63) {
whichChord = chord3;
} else if (distanceFromPelvis > 0.55) {
whichChord = chord2;
} else {
whichChord = chord1;
}
}
}
}
// Connect a call back that happens every frame
Script.update.connect(checkHands);

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var windowDimensions = Controller.getViewportDimensions();
var LASER_WIDTH = 4;
var LASER_COLOR = { red: 255, green: 0, blue: 0 };
var LASER_LENGTH_FACTOR = 1.5;
@ -16,6 +18,40 @@ var LASER_LENGTH_FACTOR = 1.5;
var LEFT = 0;
var RIGHT = 1;
var SPAWN_DISTANCE = 1;
var radiusDefault = 0.10;
var modelURLs = [
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX",
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/birarda/birarda_head.fbx",
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/pug.fbx",
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/newInvader16x16-large-purple.svo",
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/minotaur/mino_full.fbx",
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Combat_tank_V01.FBX",
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/orc.fbx",
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/slimer.fbx",
];
var toolIconUrl = "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/tools/";
var numberOfTools = 1;
var toolHeight = 50;
var toolWidth = 50;
var toolVerticalSpacing = 4;
var toolsHeight = toolHeight * numberOfTools + toolVerticalSpacing * (numberOfTools - 1);
var toolsX = windowDimensions.x - 8 - toolWidth;
var toolsY = (windowDimensions.y - toolsHeight) / 2;
var firstModel = Overlays.addOverlay("image", {
x: 0, y: 0, width: toolWidth, height: toolHeight,
subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight },
imageURL: toolIconUrl + "voxel-tool.svg",
x: toolsX, y: toolsY + ((toolHeight + toolVerticalSpacing) * 0), width: toolWidth, height: toolHeight,
visible: true,
alpha: 0.9
});
function controller(wichSide) {
this.side = wichSide;
this.palm = 2 * wichSide;
@ -46,7 +82,10 @@ function controller(wichSide) {
this.pressing = false; // is trigger being pressed (is pressed now but wasn't previously)
this.grabbing = false;
this.modelID;
this.modelID = { isKnownID: false };
this.oldModelRotation;
this.oldModelPosition;
this.oldModelRadius;
this.laser = Overlays.addOverlay("line3d", {
position: this.palmPosition,
@ -85,23 +124,19 @@ function controller(wichSide) {
this.grab = function (modelID) {
if (!modelID.isKnownID) {
var identify = Models.identifyModel(modelID);
if (!identify.isKnownID) {
print("Unknown ID " + identify.id + "(grab)");
return;
}
modelID = identify;
}
this.grab = function (modelID, properties) {
print("Grabbing " + modelID.id);
this.grabbing = true;
this.modelID = modelID;
this.oldModelPosition = properties.position;
this.oldModelRotation = properties.modelRotation;
this.oldModelRadius = properties.radius;
}
this.release = function () {
this.grabbing = false;
this.modelID = 0;
this.modelID.isKnownID = false;
}
this.checkTrigger = function () {
@ -118,6 +153,34 @@ function controller(wichSide) {
}
}
this.checkModel = function (properties) {
// P P - Model
// /| A - Palm
// / | d B - unit vector toward tip
// / | X - base of the perpendicular line
// A---X----->B d - distance fom axis
// x x - distance from A
//
// |X-A| = (P-A).B
// X == A + ((P-A).B)B
// d = |P-X|
var A = this.palmPosition;
var B = this.front;
var P = properties.position;
var x = Vec3.dot(Vec3.subtract(P, A), B);
var y = Vec3.dot(Vec3.subtract(P, A), this.up);
var z = Vec3.dot(Vec3.subtract(P, A), this.right);
var X = Vec3.sum(A, Vec3.multiply(B, x));
var d = Vec3.length(Vec3.subtract(P, X));
if (d < properties.radius && 0 < x && x < LASER_LENGTH_FACTOR) {
return { valid: true, x: x, y: y, z: z };
}
return { valid: false };
}
this.moveLaser = function () {
var endPosition = Vec3.sum(this.palmPosition, Vec3.multiply(this.front, LASER_LENGTH_FACTOR));
@ -143,44 +206,33 @@ function controller(wichSide) {
});
}
this.checkModel = function (modelID) {
if (!modelID.isKnownID) {
var identify = Models.identifyModel(modelID);
if (!identify.isKnownID) {
print("Unknown ID " + identify.id + "(checkModel)");
return;
}
modelID = identify;
this.moveModel = function () {
if (this.grabbing) {
var newPosition = Vec3.sum(this.palmPosition,
Vec3.multiply(this.front, this.x));
newPosition = Vec3.sum(newPosition,
Vec3.multiply(this.up, this.y));
newPosition = Vec3.sum(newPosition,
Vec3.multiply(this.right, this.z));
var newRotation = Quat.multiply(this.rotation,
Quat.inverse(this.oldRotation));
newRotation = Quat.multiply(newRotation,
this.oldModelRotation);
Models.editModel(this.modelID, {
position: newPosition,
modelRotation: newRotation
});
print("Moving " + this.modelID.id);
// Vec3.print("Old Position: ", this.oldModelPosition);
// Vec3.print("Sav Position: ", newPosition);
Quat.print("Old Rotation: ", this.oldModelRotation);
Quat.print("New Rotation: ", newRotation);
this.oldModelRotation = newRotation;
this.oldModelPosition = newPosition;
}
// P P - Model
// /| A - Palm
// / | d B - unit vector toward tip
// / | X - base of the perpendicular line
// A---X----->B d - distance fom axis
// x x - distance from A
//
// |X-A| = (P-A).B
// X == A + ((P-A).B)B
// d = |P-X|
var A = this.palmPosition;
var B = this.front;
var P = Models.getModelProperties(modelID).position;
this.x = Vec3.dot(Vec3.subtract(P, A), B);
this.y = Vec3.dot(Vec3.subtract(P, A), this.up);
this.z = Vec3.dot(Vec3.subtract(P, A), this.right);
var X = Vec3.sum(A, Vec3.multiply(B, this.x));
var d = Vec3.length(Vec3.subtract(P, X));
// Vec3.print("A: ", A);
// Vec3.print("B: ", B);
// Vec3.print("Particle pos: ", P);
// print("d: " + d + ", x: " + this.x);
if (d < Models.getModelProperties(modelID).radius && 0 < this.x && this.x < LASER_LENGTH_FACTOR) {
return true;
}
return false;
}
this.update = function () {
@ -205,25 +257,40 @@ function controller(wichSide) {
this.checkTrigger();
if (this.pressing) {
Vec3.print("Looking at: ", this.palmPosition);
var foundModels = Models.findModels(this.palmPosition, LASER_LENGTH_FACTOR);
for (var i = 0; i < foundModels.length; i++) {
print("Model found ID (" + foundModels[i].id + ")");
if (this.checkModel(foundModels[i])) {
if (this.grab(foundModels[i])) {
return;
}
}
}
}
this.moveLaser();
if (!this.pressed && this.grabbing) {
// release if trigger not pressed anymore.
this.release();
}
this.moveLaser();
if (this.pressing) {
Vec3.print("Looking at: ", this.palmPosition);
var foundModels = Models.findModels(this.palmPosition, LASER_LENGTH_FACTOR);
for (var i = 0; i < foundModels.length; i++) {
if (!foundModels[i].isKnownID) {
var identify = Models.identifyModel(foundModels[i]);
if (!identify.isKnownID) {
print("Unknown ID " + identify.id + "(update loop)");
return;
}
foundModels[i] = identify;
}
var properties = Models.getModelProperties(foundModels[i]);
print("Checking properties: " + properties.id + " " + properties.isKnownID);
var check = this.checkModel(properties);
if (check.valid) {
this.grab(foundModels[i], properties);
this.x = check.x;
this.y = check.y;
this.z = check.z;
return;
}
}
}
}
this.cleanup = function () {
@ -238,78 +305,44 @@ var leftController = new controller(LEFT);
var rightController = new controller(RIGHT);
function moveModels() {
if (leftController.grabbing) {
if (rightController.grabbing) {
var properties = Models.getModelProperties(leftController.modelID);
var oldLeftPoint = Vec3.sum(leftController.oldPalmPosition, Vec3.multiply(leftController.oldFront, leftController.x));
var oldRightPoint = Vec3.sum(rightController.oldPalmPosition, Vec3.multiply(rightController.oldFront, rightController.x));
var oldMiddle = Vec3.multiply(Vec3.sum(oldLeftPoint, oldRightPoint), 0.5);
var oldLength = Vec3.length(Vec3.subtract(oldLeftPoint, oldRightPoint));
var leftPoint = Vec3.sum(leftController.palmPosition, Vec3.multiply(leftController.front, leftController.x));
var rightPoint = Vec3.sum(rightController.palmPosition, Vec3.multiply(rightController.front, rightController.x));
var middle = Vec3.multiply(Vec3.sum(leftPoint, rightPoint), 0.5);
var length = Vec3.length(Vec3.subtract(leftPoint, rightPoint));
var ratio = length / oldLength;
var newPosition = Vec3.sum(middle,
Vec3.multiply(Vec3.subtract(properties.position, oldMiddle), ratio));
Vec3.print("Ratio : " + ratio + " New position: ", newPosition);
var rotation = Quat.multiply(leftController.rotation,
Quat.inverse(leftController.oldRotation));
rotation = Quat.multiply(rotation, properties.modelRotation);
Models.editModel(leftController.modelID, {
position: newPosition,
//modelRotation: rotation,
radius: properties.radius * ratio
});
return;
} else {
var newPosition = Vec3.sum(leftController.palmPosition,
Vec3.multiply(leftController.front, leftController.x));
newPosition = Vec3.sum(newPosition,
Vec3.multiply(leftController.up, leftController.y));
newPosition = Vec3.sum(newPosition,
Vec3.multiply(leftController.right, leftController.z));
var rotation = Quat.multiply(leftController.rotation,
Quat.inverse(leftController.oldRotation));
rotation = Quat.multiply(rotation,
Models.getModelProperties(leftController.modelID).modelRotation);
Models.editModel(leftController.modelID, {
position: newPosition,
modelRotation: rotation
});
}
}
if (rightController.grabbing) {
var newPosition = Vec3.sum(rightController.palmPosition,
Vec3.multiply(rightController.front, rightController.x));
newPosition = Vec3.sum(newPosition,
Vec3.multiply(rightController.up, rightController.y));
newPosition = Vec3.sum(newPosition,
Vec3.multiply(rightController.right, rightController.z));
if (leftController.grabbing && rightController.grabbing && rightController.modelID.id == leftController.modelID.id) {
print("Both controllers");
var oldLeftPoint = Vec3.sum(leftController.oldPalmPosition, Vec3.multiply(leftController.oldFront, leftController.x));
var oldRightPoint = Vec3.sum(rightController.oldPalmPosition, Vec3.multiply(rightController.oldFront, rightController.x));
var rotation = Quat.multiply(rightController.rotation,
Quat.inverse(rightController.oldRotation));
rotation = Quat.multiply(rotation,
Models.getModelProperties(rightController.modelID).modelRotation);
var oldMiddle = Vec3.multiply(Vec3.sum(oldLeftPoint, oldRightPoint), 0.5);
var oldLength = Vec3.length(Vec3.subtract(oldLeftPoint, oldRightPoint));
Models.editModel(rightController.modelID, {
var leftPoint = Vec3.sum(leftController.palmPosition, Vec3.multiply(leftController.front, leftController.x));
var rightPoint = Vec3.sum(rightController.palmPosition, Vec3.multiply(rightController.front, rightController.x));
var middle = Vec3.multiply(Vec3.sum(leftPoint, rightPoint), 0.5);
var length = Vec3.length(Vec3.subtract(leftPoint, rightPoint));
var ratio = length / oldLength;
var newPosition = Vec3.sum(middle,
Vec3.multiply(Vec3.subtract(leftController.oldModelPosition, oldMiddle), ratio));
Vec3.print("Ratio : " + ratio + " New position: ", newPosition);
var rotation = Quat.multiply(leftController.rotation,
Quat.inverse(leftController.oldRotation));
rotation = Quat.multiply(rotation, leftController.oldModelRotation);
Models.editModel(leftController.modelID, {
position: newPosition,
modelRotation: rotation
//modelRotation: rotation,
radius: leftController.oldModelRadius * ratio
});
leftController.oldModelPosition = newPosition;
leftController.oldModelRotation = rotation;
leftController.oldModelRadius *= ratio;
return;
}
leftController.moveModel();
rightController.moveModel();
}
function checkController(deltaTime) {
@ -318,6 +351,8 @@ function checkController(deltaTime) {
var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
moveOverlays();
// this is expected for hydras
if (!(numberOfButtons==12 && numberOfTriggers == 2 && controllersPerTrigger == 2)) {
//print("no hydra connected?");
@ -329,14 +364,48 @@ function checkController(deltaTime) {
moveModels();
}
function moveOverlays() {
windowDimensions = Controller.getViewportDimensions();
toolsX = windowDimensions.x - 8 - toolWidth;
toolsY = (windowDimensions.y - toolsHeight) / 2;
Overlays.editOverlay(firstModel, {
x: toolsX, y: toolsY + ((toolHeight + toolVerticalSpacing) * 0), width: toolWidth, height: toolHeight,
});
}
function mousePressEvent(event) {
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
var url;
if (clickedOverlay == firstModel) {
url = Window.prompt("Model url", modelURLs[Math.floor(Math.random() * modelURLs.length)]);
if (url == null) {
return; }
} else {
print("Didn't click on anything");
return;
}
var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE));
Models.addModel({ position: position,
radius: radiusDefault,
modelURL: url
});
}
function scriptEnding() {
leftController.cleanup();
rightController.cleanup();
Overlays.deleteOverlay(firstModel);
}
Script.scriptEnding.connect(scriptEnding);
// register the call back so it fires before each data send
Script.update.connect(checkController);
Controller.mousePressEvent.connect(mousePressEvent);

View file

@ -31,14 +31,14 @@ var grabbingWithLeftHand = false;
var wasGrabbingWithLeftHand = false;
var EPSILON = 0.000001;
var velocity = { x: 0, y: 0, z: 0};
var THRUST_MAG_UP = 800.0;
var THRUST_MAG_DOWN = 300.0;
var THRUST_MAG_FWD = 500.0;
var THRUST_MAG_BACK = 300.0;
var THRUST_MAG_LATERAL = 250.0;
var THRUST_MAG_UP = 100.0;
var THRUST_MAG_DOWN = 100.0;
var THRUST_MAG_FWD = 150.0;
var THRUST_MAG_BACK = 100.0;
var THRUST_MAG_LATERAL = 150.0;
var THRUST_JUMP = 120.0;
var YAW_MAG = 500.0;
var YAW_MAG = 100.0;
var PITCH_MAG = 100.0;
var THRUST_MAG_HAND_JETS = THRUST_MAG_FWD;
var JOYSTICK_YAW_MAG = YAW_MAG;

View file

@ -37,7 +37,7 @@ var radiusMinimum = 0.05;
var radiusMaximum = 0.5;
var modelURLs = [
"https://s3-us-west-1.amazonaws.com/highfidelity-public/models/music/EVHFrankenstein.fbx",
"https://s3-us-west-1.amazonaws.com/highfidelity-public/models/attachments/topHat.fst",
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX",
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/birarda/birarda_head.fbx",
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/pug.fbx",
@ -72,7 +72,6 @@ function keyPressEvent(event) {
}
} else if (event.text == "m") {
var URL = Window.prompt("Model URL", "Enter URL, e.g. http://foo.com/model.fbx");
Window.alert("Your response was: " + prompt);
var modelPosition = getNewVoxelPosition();
var properties = { position: { x: modelPosition.x,
y: modelPosition.y,

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,47 @@
#version 120
//
// model_normal_specular_map.frag
// fragment shader
//
// Created by Andrzej Kapolka on 5/6/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
//
// the diffuse texture
uniform sampler2D diffuseMap;
// the normal map texture
uniform sampler2D normalMap;
// the specular map texture
uniform sampler2D specularMap;
// the interpolated normal
varying vec4 interpolatedNormal;
// the interpolated tangent
varying vec4 interpolatedTangent;
void main(void) {
vec3 normalizedNormal = normalize(vec3(interpolatedNormal));
vec3 normalizedTangent = normalize(vec3(interpolatedTangent));
vec3 normalizedBitangent = normalize(cross(normalizedNormal, normalizedTangent));
vec3 localNormal = vec3(texture2D(normalMap, gl_TexCoord[0].st)) * 2.0 - vec3(1.0, 1.0, 1.0);
// compute the base color based on OpenGL lighting model
vec4 viewNormal = vec4(normalizedTangent * localNormal.x +
normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0);
vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
gl_FrontLightProduct[0].diffuse * max(0.0, dot(viewNormal, gl_LightSource[0].position)));
// compute the specular component (sans exponent)
float specular = max(0.0, dot(normalize(gl_LightSource[0].position + vec4(0.0, 0.0, 1.0, 0.0)), viewNormal));
// modulate texture by base color and add specular contribution
gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) + vec4(pow(specular, gl_FrontMaterial.shininess) *
gl_FrontLightProduct[0].specular.rgb * texture2D(specularMap, gl_TexCoord[0].st).rgb, 0.0);
}

View file

@ -0,0 +1,35 @@
#version 120
//
// model_specular_map.frag
// fragment shader
//
// Created by Andrzej Kapolka on 5/6/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
//
// the diffuse texture
uniform sampler2D diffuseMap;
// the specular texture
uniform sampler2D specularMap;
// the interpolated normal
varying vec4 normal;
void main(void) {
// compute the base color based on OpenGL lighting model
vec4 normalizedNormal = normalize(normal);
vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
gl_FrontLightProduct[0].diffuse * max(0.0, dot(normalizedNormal, gl_LightSource[0].position)));
// compute the specular component (sans exponent)
float specular = max(0.0, dot(normalize(gl_LightSource[0].position + vec4(0.0, 0.0, 1.0, 0.0)), normalizedNormal));
// modulate texture by base color and add specular contribution
gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) + vec4(pow(specular, gl_FrontMaterial.shininess) *
gl_FrontLightProduct[0].specular.rgb * texture2D(specularMap, gl_TexCoord[0].st).rgb, 0.0);
}

View file

@ -151,6 +151,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
_lastQueriedTime(usecTimestampNow()),
_mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)),
_cameraPushback(0.0f),
_scaleMirror(1.0f),
_mouseX(0),
_mouseY(0),
_lastMouseMove(usecTimestampNow()),
@ -570,9 +571,9 @@ void Application::paintGL() {
_myCamera.setTightness(0.0f);
glm::vec3 eyePosition = _myAvatar->getHead()->calculateAverageEyePosition();
float headHeight = eyePosition.y - _myAvatar->getPosition().y;
_myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _myAvatar->getScale());
_myCamera.setTargetPosition(_myAvatar->getPosition() + glm::vec3(0, headHeight, 0));
_myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f)));
_myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _myAvatar->getScale() * _scaleMirror);
_myCamera.setTargetPosition(_myAvatar->getPosition() + glm::vec3(0, headHeight + (_raiseMirror * _myAvatar->getScale()), 0));
_myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)));
// if the head would intersect the near clip plane, we must push the camera out
glm::vec3 relativePosition = glm::inverse(_myCamera.getTargetRotation()) *
@ -868,19 +869,43 @@ void Application::keyPressEvent(QKeyEvent* event) {
break;
case Qt::Key_Up:
_myAvatar->setDriveKeys(isShifted ? UP : FWD, 1.f);
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
if (!isShifted) {
_scaleMirror *= 0.95f;
} else {
_raiseMirror += 0.05f;
}
} else {
_myAvatar->setDriveKeys(isShifted ? UP : FWD, 1.f);
}
break;
case Qt::Key_Down:
_myAvatar->setDriveKeys(isShifted ? DOWN : BACK, 1.f);
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
if (!isShifted) {
_scaleMirror *= 1.05f;
} else {
_raiseMirror -= 0.05f;
}
} else {
_myAvatar->setDriveKeys(isShifted ? DOWN : BACK, 1.f);
}
break;
case Qt::Key_Left:
_myAvatar->setDriveKeys(isShifted ? LEFT : ROT_LEFT, 1.f);
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
_rotateMirror += PI / 20.f;
} else {
_myAvatar->setDriveKeys(isShifted ? LEFT : ROT_LEFT, 1.f);
}
break;
case Qt::Key_Right:
_myAvatar->setDriveKeys(isShifted ? RIGHT : ROT_RIGHT, 1.f);
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
_rotateMirror -= PI / 20.f;
} else {
_myAvatar->setDriveKeys(isShifted ? RIGHT : ROT_RIGHT, 1.f);
}
break;
case Qt::Key_I:
@ -1192,10 +1217,6 @@ void Application::touchBeginEvent(QTouchEvent* event) {
return;
}
// put any application specific touch behavior below here..
_lastTouchAvgX = _touchAvgX;
_lastTouchAvgY = _touchAvgY;
}
void Application::touchEndEvent(QTouchEvent* event) {
@ -1850,34 +1871,6 @@ void Application::updateMyAvatarLookAtPosition() {
_myAvatar->getHead()->setLookAtPosition(lookAtSpot);
}
void Application::updateHandAndTouch(float deltaTime) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateHandAndTouch()");
// Update from Touch
if (_isTouchPressed) {
_lastTouchAvgX = _touchAvgX;
_lastTouchAvgY = _touchAvgY;
}
}
void Application::updateLeap(float deltaTime) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateLeap()");
}
void Application::updateSixense(float deltaTime) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateSixense()");
_sixenseManager.update(deltaTime);
}
void Application::updateSerialDevices(float deltaTime) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateSerialDevices()");
}
void Application::updateThreads(float deltaTime) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateThreads()");
@ -1991,11 +1984,7 @@ void Application::update(float deltaTime) {
updateVisage();
_myAvatar->updateLookAtTargetAvatar();
updateMyAvatarLookAtPosition();
updateHandAndTouch(deltaTime); // Update state for touch sensors
updateLeap(deltaTime); // Leap finger-sensing device
updateSixense(deltaTime); // Razer Hydra controllers
updateSerialDevices(deltaTime); // Read serial port interface devices
_sixenseManager.update(deltaTime);
updateMyAvatar(deltaTime); // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
_avatarManager.updateOtherAvatars(deltaTime); //loop through all the other avatars and simulate them...
@ -2348,8 +2337,8 @@ void Application::updateShadowMap() {
updateUntranslatedViewMatrix();
_avatarManager.renderAvatars(Avatar::SHADOW_RENDER_MODE);
_particles.render();
_models.render();
_particles.render(OctreeRenderer::SHADOW_RENDER_MODE);
_models.render(OctreeRenderer::SHADOW_RENDER_MODE);
glPopMatrix();
@ -2843,7 +2832,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) {
// save absolute translations
glm::vec3 absoluteSkeletonTranslation = _myAvatar->getSkeletonModel().getTranslation();
glm::vec3 absoluteFaceTranslation = _myAvatar->getHead()->getFaceModel().getTranslation();
// get the eye positions relative to the neck and use them to set the face translation
glm::vec3 leftEyePosition, rightEyePosition;
_myAvatar->getHead()->getFaceModel().setTranslation(glm::vec3());
@ -2857,11 +2846,22 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) {
_myAvatar->getSkeletonModel().setTranslation(_myAvatar->getHead()->getFaceModel().getTranslation() -
neckPosition);
// update the attachments to match
QVector<glm::vec3> absoluteAttachmentTranslations;
glm::vec3 delta = _myAvatar->getSkeletonModel().getTranslation() - absoluteSkeletonTranslation;
foreach (Model* attachment, _myAvatar->getAttachmentModels()) {
absoluteAttachmentTranslations.append(attachment->getTranslation());
attachment->setTranslation(attachment->getTranslation() + delta);
}
displaySide(_mirrorCamera, true);
// restore absolute translations
_myAvatar->getSkeletonModel().setTranslation(absoluteSkeletonTranslation);
_myAvatar->getHead()->getFaceModel().setTranslation(absoluteFaceTranslation);
for (int i = 0; i < absoluteAttachmentTranslations.size(); i++) {
_myAvatar->getAttachmentModels().at(i)->setTranslation(absoluteAttachmentTranslations.at(i));
}
} else {
displaySide(_mirrorCamera, true);
}

View file

@ -343,10 +343,6 @@ private:
void updateFaceshift();
void updateVisage();
void updateMyAvatarLookAtPosition();
void updateHandAndTouch(float deltaTime);
void updateLeap(float deltaTime);
void updateSixense(float deltaTime);
void updateSerialDevices(float deltaTime);
void updateThreads(float deltaTime);
void updateMetavoxels(float deltaTime);
void updateCamera(float deltaTime);
@ -455,6 +451,10 @@ private:
glm::mat4 _untranslatedViewMatrix;
glm::vec3 _viewMatrixTranslation;
glm::mat4 _projectionMatrix;
float _scaleMirror;
float _rotateMirror;
float _raiseMirror;
glm::mat4 _shadowMatrix;
@ -473,8 +473,6 @@ private:
float _touchAvgX;
float _touchAvgY;
float _lastTouchAvgX;
float _lastTouchAvgY;
float _touchDragStartedAvgX;
float _touchDragStartedAvgY;
bool _isTouchPressed; // true if multitouch has been pressed (clear when finished)

View file

@ -1068,6 +1068,7 @@ void Audio::toggleScope() {
memset(_scopeInput.data(), 0, width * sizeof(int16_t));
memset(_scopeOutputLeft.data(), 0, width * sizeof(int16_t));
memset(_scopeOutputRight.data(), 0, width * sizeof(int16_t));
_scopeEnabledPause = false;
}
}

View file

@ -131,6 +131,20 @@ void DatagramProcessor::processDatagrams() {
break;
}
case PacketTypeMuteEnvironment: {
glm::vec3 position;
float radius;
int headerSize = numBytesForPacketHeaderGivenPacketType(PacketTypeMuteEnvironment);
memcpy(&position, incomingPacket.constData() + headerSize, sizeof(glm::vec3));
memcpy(&radius, incomingPacket.constData() + headerSize + sizeof(glm::vec3), sizeof(float));
if (glm::distance(Application::getInstance()->getAvatar()->getPosition(), position) < radius
&& !Application::getInstance()->getAudio()->getMuted()) {
Application::getInstance()->getAudio()->toggleMute();
}
break;
}
default:
nodeList->processNodeData(senderSockAddr, incomingPacket);
break;

View file

@ -68,6 +68,7 @@ const float DEFAULT_FACESHIFT_EYE_DEFLECTION = 0.25f;
const float DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER = 1.0f;
const int ONE_SECOND_OF_FRAMES = 60;
const int FIVE_SECONDS_OF_FRAMES = 5 * ONE_SECOND_OF_FRAMES;
const float MUTE_RADIUS = 50;
Menu::Menu() :
_actionHash(),
@ -271,9 +272,6 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Bandwidth, 0, true);
addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0, this, SLOT(bandwidthDetails()));
addActionToQMenuAndActionHash(viewMenu, MenuOption::OctreeStats, 0, this, SLOT(octreeStatsDetails()));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::AudioScope, 0, false,
appInstance->getAudio(),
SLOT(toggleScope()));
QMenu* developerMenu = addMenu("Developer");
@ -291,7 +289,6 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Metavoxels, 0, true);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::BuckyBalls, 0, false);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Particles, 0, true);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Models, 0, true);
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, Qt::SHIFT | Qt::Key_L, this, SLOT(lodTools()));
QMenu* voxelOptionsMenu = developerMenu->addMenu("Voxel Options");
@ -308,6 +305,12 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DontFadeOnVoxelServerChanges);
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DisableAutoAdjustLOD);
QMenu* modelOptionsMenu = developerMenu->addMenu("Model Options");
addCheckableActionToQMenuAndActionHash(modelOptionsMenu, MenuOption::Models, 0, true);
addCheckableActionToQMenuAndActionHash(modelOptionsMenu, MenuOption::DisplayModelBounds, 0, false);
addCheckableActionToQMenuAndActionHash(modelOptionsMenu, MenuOption::DisplayModelElementProxy, 0, false);
addCheckableActionToQMenuAndActionHash(modelOptionsMenu, MenuOption::DisplayModelElementChildProxies, 0, false);
QMenu* avatarOptionsMenu = developerMenu->addMenu("Avatar Options");
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Avatars, 0, true);
@ -370,20 +373,6 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::PipelineWarnings);
addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::SuppressShortTimings);
addCheckableActionToQMenuAndActionHash(renderDebugMenu,
MenuOption::CullSharedFaces,
Qt::CTRL | Qt::SHIFT | Qt::Key_C,
false,
appInstance->getVoxels(),
SLOT(cullSharedFaces()));
addCheckableActionToQMenuAndActionHash(renderDebugMenu,
MenuOption::ShowCulledSharedFaces,
0,
false,
appInstance->getVoxels(),
SLOT(showCulledSharedFaces()));
QMenu* audioDebugMenu = developerMenu->addMenu("Audio Debugging Tools");
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNoiseReduction,
0,
@ -397,13 +386,21 @@ Menu::Menu() :
false,
appInstance->getAudio(),
SLOT(toggleMute()));
addActionToQMenuAndActionHash(audioDebugMenu,
MenuOption::MuteEnvironment,
0,
this,
SLOT(muteEnvironment()));
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioToneInjection,
0,
false,
appInstance->getAudio(),
SLOT(toggleToneInjection()));
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScope, Qt::CTRL | Qt::Key_P, false,
appInstance->getAudio(),
SLOT(toggleScope()));
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScopePause,
Qt::CTRL | Qt::Key_P,
Qt::CTRL | Qt::SHIFT | Qt::Key_P ,
false,
appInstance->getAudio(),
SLOT(toggleScopePause()));
@ -1000,6 +997,30 @@ void Menu::multipleDestinationsDecision(const QJsonObject& userData, const QJson
disconnect(manager, &LocationManager::multipleDestinationsFound, this, &Menu::multipleDestinationsDecision);
}
void Menu::muteEnvironment() {
int headerSize = numBytesForPacketHeaderGivenPacketType(PacketTypeMuteEnvironment);
int packetSize = headerSize + sizeof(glm::vec3) + sizeof(float);
glm::vec3 position = Application::getInstance()->getAvatar()->getPosition();
char* packet = (char*)malloc(packetSize);
populatePacketHeader(packet, PacketTypeMuteEnvironment);
memcpy(packet + headerSize, &position, sizeof(glm::vec3));
memcpy(packet + headerSize + sizeof(glm::vec3), &MUTE_RADIUS, sizeof(float));
QByteArray mutePacket(packet, packetSize);
// grab our audio mixer from the NodeList, if it exists
SharedNodePointer audioMixer = NodeList::getInstance()->soloNodeOfType(NodeType::AudioMixer);
if (audioMixer) {
// send off this mute packet
NodeList::getInstance()->writeDatagram(mutePacket, audioMixer);
}
free(packet);
}
void Menu::goToLocation() {
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
glm::vec3 avatarPos = myAvatar->getPosition();

View file

@ -191,6 +191,7 @@ private slots:
void audioMuteToggled();
void namedLocationCreated(LocationManager::NamedLocationCreateResponse response);
void multipleDestinationsDecision(const QJsonObject& userData, const QJsonObject& placeData);
void muteEnvironment();
private:
static Menu* _instance;
@ -296,13 +297,15 @@ namespace MenuOption {
const QString CollideWithParticles = "Collide With Particles";
const QString CollideWithVoxels = "Collide With Voxels";
const QString Collisions = "Collisions";
const QString CullSharedFaces = "Cull Shared Voxel Faces";
const QString DecreaseAvatarSize = "Decrease Avatar Size";
const QString DecreaseVoxelSize = "Decrease Voxel Size";
const QString DisableAutoAdjustLOD = "Disable Automatically Adjusting LOD";
const QString DisplayFrustum = "Display Frustum";
const QString DisplayHands = "Display Hands";
const QString DisplayHandTargets = "Display Hand Targets";
const QString DisplayModelBounds = "Display Model Bounds";
const QString DisplayModelElementProxy = "Display Model Element Bounds";
const QString DisplayModelElementChildProxies = "Display Model Element Children";
const QString DontFadeOnVoxelServerChanges = "Don't Fade In/Out on Voxel Server Changes";
const QString EchoLocalAudio = "Echo Local Audio";
const QString EchoServerAudio = "Echo Server Audio";
@ -337,8 +340,10 @@ namespace MenuOption {
const QString Metavoxels = "Metavoxels";
const QString Mirror = "Mirror";
const QString Models = "Models";
const QString ModelOptions = "Model Options";
const QString MoveWithLean = "Move with Lean";
const QString MuteAudio = "Mute Microphone";
const QString MuteEnvironment = "Mute Environment";
const QString NameLocation = "Name this location";
const QString NewVoxelCullingMode = "New Voxel Culling Mode";
const QString OctreeStats = "Voxel and Particle Statistics";
@ -361,7 +366,6 @@ namespace MenuOption {
const QString SettingsExport = "Export Settings";
const QString SettingsImport = "Import Settings";
const QString Shadows = "Shadows";
const QString ShowCulledSharedFaces = "Show Culled Shared Voxel Faces";
const QString ShowIKConstraints = "Show IK Constraints";
const QString Stars = "Stars";
const QString Stats = "Stats";

View file

@ -9,6 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QCheckBox>
#include <QComboBox>
#include <QDebug>
#include <QDialogButtonBox>
@ -42,6 +43,9 @@ static const QString TEXDIR_FIELD = "texdir";
static const QString LOD_FIELD = "lod";
static const QString JOINT_INDEX_FIELD = "jointIndex";
static const QString SCALE_FIELD = "scale";
static const QString TRANSLATION_X_FIELD = "tx";
static const QString TRANSLATION_Y_FIELD = "ty";
static const QString TRANSLATION_Z_FIELD = "tz";
static const QString JOINT_FIELD = "joint";
static const QString FREE_JOINT_FIELD = "freeJoint";
@ -424,19 +428,32 @@ void ModelUploader::processCheck() {
}
bool ModelUploader::addTextures(const QString& texdir, const FBXGeometry& geometry) {
QSet<QByteArray> added;
foreach (FBXMesh mesh, geometry.meshes) {
foreach (FBXMeshPart part, mesh.parts) {
if (!part.diffuseTexture.filename.isEmpty() && part.diffuseTexture.content.isEmpty()) {
if (!part.diffuseTexture.filename.isEmpty() && part.diffuseTexture.content.isEmpty() &&
!added.contains(part.diffuseTexture.filename)) {
if (!addPart(texdir + "/" + part.diffuseTexture.filename,
QString("texture%1").arg(++_texturesCount), true)) {
return false;
}
added.insert(part.diffuseTexture.filename);
}
if (!part.normalTexture.filename.isEmpty() && part.normalTexture.content.isEmpty()) {
if (!part.normalTexture.filename.isEmpty() && part.normalTexture.content.isEmpty() &&
!added.contains(part.normalTexture.filename)) {
if (!addPart(texdir + "/" + part.normalTexture.filename,
QString("texture%1").arg(++_texturesCount), true)) {
return false;
}
added.insert(part.normalTexture.filename);
}
if (!part.specularTexture.filename.isEmpty() && part.specularTexture.content.isEmpty() &&
!added.contains(part.specularTexture.filename)) {
if (!addPart(texdir + "/" + part.specularTexture.filename,
QString("texture%1").arg(++_texturesCount), true)) {
return false;
}
added.insert(part.specularTexture.filename);
}
}
}
@ -506,6 +523,14 @@ bool ModelUploader::addPart(const QFile& file, const QByteArray& contents, const
return true;
}
static QDoubleSpinBox* createTranslationBox() {
QDoubleSpinBox* box = new QDoubleSpinBox();
const double MAX_TRANSLATION = 1000000.0;
box->setMinimum(-MAX_TRANSLATION);
box->setMaximum(MAX_TRANSLATION);
return box;
}
ModelPropertiesDialog::ModelPropertiesDialog(ModelType modelType, const QVariantHash& originalMapping,
const QString& basePath, const FBXGeometry& geometry) :
_modelType(modelType),
@ -527,7 +552,18 @@ ModelPropertiesDialog::ModelPropertiesDialog(ModelType modelType, const QVariant
_scale->setMaximum(FLT_MAX);
_scale->setSingleStep(0.01);
if (_modelType != ATTACHMENT_MODEL) {
if (_modelType == ATTACHMENT_MODEL) {
QHBoxLayout* translation = new QHBoxLayout();
form->addRow("Translation:", translation);
translation->addWidget(_translationX = createTranslationBox());
translation->addWidget(_translationY = createTranslationBox());
translation->addWidget(_translationZ = createTranslationBox());
form->addRow("Pivot About Center:", _pivotAboutCenter = new QCheckBox());
form->addRow("Pivot Joint:", _pivotJoint = createJointBox());
connect(_pivotAboutCenter, SIGNAL(toggled(bool)), SLOT(updatePivotJoint()));
_pivotAboutCenter->setChecked(true);
} else {
form->addRow("Left Eye Joint:", _leftEyeJoint = createJointBox());
form->addRow("Right Eye Joint:", _rightEyeJoint = createJointBox());
form->addRow("Neck Joint:", _neckJoint = createJointBox());
@ -571,7 +607,19 @@ QVariantHash ModelPropertiesDialog::getMapping() const {
mapping.insert(JOINT_INDEX_FIELD, jointIndices);
QVariantHash joints = mapping.value(JOINT_FIELD).toHash();
if (_modelType != ATTACHMENT_MODEL) {
if (_modelType == ATTACHMENT_MODEL) {
glm::vec3 pivot;
if (_pivotAboutCenter->isChecked()) {
pivot = (_geometry.meshExtents.minimum + _geometry.meshExtents.maximum) * 0.5f;
} else if (_pivotJoint->currentIndex() != 0) {
pivot = extractTranslation(_geometry.joints.at(_pivotJoint->currentIndex() - 1).transform);
}
mapping.insert(TRANSLATION_X_FIELD, -pivot.x * _scale->value() + _translationX->value());
mapping.insert(TRANSLATION_Y_FIELD, -pivot.y * _scale->value() + _translationY->value());
mapping.insert(TRANSLATION_Z_FIELD, -pivot.z * _scale->value() + _translationZ->value());
} else {
insertJointMapping(joints, "jointEyeLeft", _leftEyeJoint->currentText());
insertJointMapping(joints, "jointEyeRight", _rightEyeJoint->currentText());
insertJointMapping(joints, "jointNeck", _neckJoint->currentText());
@ -604,7 +652,14 @@ void ModelPropertiesDialog::reset() {
_scale->setValue(_originalMapping.value(SCALE_FIELD).toDouble());
QVariantHash jointHash = _originalMapping.value(JOINT_FIELD).toHash();
if (_modelType != ATTACHMENT_MODEL) {
if (_modelType == ATTACHMENT_MODEL) {
_translationX->setValue(_originalMapping.value(TRANSLATION_X_FIELD).toDouble());
_translationY->setValue(_originalMapping.value(TRANSLATION_Y_FIELD).toDouble());
_translationZ->setValue(_originalMapping.value(TRANSLATION_Z_FIELD).toDouble());
_pivotAboutCenter->setChecked(true);
_pivotJoint->setCurrentIndex(0);
} else {
setJointText(_leftEyeJoint, jointHash.value("jointEyeLeft").toString());
setJointText(_rightEyeJoint, jointHash.value("jointEyeRight").toString());
setJointText(_neckJoint, jointHash.value("jointNeck").toString());
@ -641,6 +696,10 @@ void ModelPropertiesDialog::chooseTextureDirectory() {
_textureDirectory->setText(directory.length() == _basePath.length() ? "." : directory.mid(_basePath.length() + 1));
}
void ModelPropertiesDialog::updatePivotJoint() {
_pivotJoint->setEnabled(!_pivotAboutCenter->isChecked());
}
void ModelPropertiesDialog::createNewFreeJoint(const QString& joint) {
QWidget* freeJoint = new QWidget();
QHBoxLayout* freeJointLayout = new QHBoxLayout();

View file

@ -19,6 +19,7 @@
#include "ui/ModelsBrowser.h"
class QCheckBox;
class QComboBox;
class QDoubleSpinBox;
class QFileInfo;
@ -83,6 +84,7 @@ public:
private slots:
void reset();
void chooseTextureDirectory();
void updatePivotJoint();
void createNewFreeJoint(const QString& joint = QString());
private:
@ -96,6 +98,11 @@ private:
QLineEdit* _name;
QPushButton* _textureDirectory;
QDoubleSpinBox* _scale;
QDoubleSpinBox* _translationX;
QDoubleSpinBox* _translationY;
QDoubleSpinBox* _translationZ;
QCheckBox* _pivotAboutCenter;
QComboBox* _pivotJoint;
QComboBox* _leftEyeJoint;
QComboBox* _rightEyeJoint;
QComboBox* _neckJoint;

View file

@ -215,17 +215,20 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) {
if (Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) {
renderBody(renderMode, glowLevel);
}
if (Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes)) {
if (renderMode != SHADOW_RENDER_MODE &&
Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes)) {
_skeletonModel.updateShapePositions();
_skeletonModel.renderJointCollisionShapes(0.7f);
}
if (Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionShapes)) {
if (renderMode != SHADOW_RENDER_MODE &&
Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionShapes)) {
if (shouldRenderHead(cameraPosition, renderMode)) {
getHead()->getFaceModel().updateShapePositions();
getHead()->getFaceModel().renderJointCollisionShapes(0.7f);
}
}
if (Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes)) {
if (renderMode != SHADOW_RENDER_MODE &&
Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes)) {
if (shouldRenderHead(cameraPosition, renderMode)) {
getHead()->getFaceModel().updateShapePositions();
getHead()->getFaceModel().renderBoundingCollisionShapes(0.7f);
@ -234,7 +237,7 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) {
}
}
// If this is the avatar being looked at, render a little ball above their head
if (_isLookAtTarget) {
if (renderMode != SHADOW_RENDER_MODE &&_isLookAtTarget) {
const float LOOK_AT_INDICATOR_RADIUS = 0.03f;
const float LOOK_AT_INDICATOR_HEIGHT = 0.60f;
const float LOOK_AT_INDICATOR_COLOR[] = { 0.8f, 0.0f, 0.0f, 0.5f };
@ -340,7 +343,8 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
void Avatar::renderBody(RenderMode renderMode, float glowLevel) {
Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ?
Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
{
Glower glower(glowLevel);
@ -351,7 +355,7 @@ void Avatar::renderBody(RenderMode renderMode, float glowLevel) {
}
_skeletonModel.render(1.0f, modelRenderMode);
renderAttachments(modelRenderMode);
getHand()->render(false);
getHand()->render(false, modelRenderMode);
}
getHead()->render(1.0f, modelRenderMode);
}
@ -369,7 +373,7 @@ void Avatar::simulateAttachments(float deltaTime) {
glm::quat jointRotation;
if (_skeletonModel.getJointPosition(jointIndex, jointPosition) &&
_skeletonModel.getJointRotation(jointIndex, jointRotation)) {
model->setTranslation(jointPosition + jointRotation * attachment.translation * _skeletonModel.getScale());
model->setTranslation(jointPosition + jointRotation * attachment.translation * _scale);
model->setRotation(jointRotation * attachment.rotation);
model->setScale(_skeletonModel.getScale() * attachment.scale);
model->simulate(deltaTime);

View file

@ -83,6 +83,7 @@ public:
//getters
bool isInitialized() const { return _initialized; }
SkeletonModel& getSkeletonModel() { return _skeletonModel; }
const QVector<Model*>& getAttachmentModels() const { return _attachmentModels; }
glm::vec3 getChestPosition() const;
float getScale() const { return _scale; }
const glm::vec3& getVelocity() const { return _velocity; }

View file

@ -192,11 +192,11 @@ void Hand::calculateGeometry() {
}
}
void Hand::render(bool isMine) {
void Hand::render(bool isMine, Model::RenderMode renderMode) {
_renderAlpha = 1.0;
if (Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes)) {
if (renderMode != Model::SHADOW_RENDER_MODE &&
Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes)) {
// draw a green sphere at hand joint location, which is actually near the wrist)
for (size_t i = 0; i < getNumPalms(); i++) {
PalmData& palm = getPalms()[i];
@ -212,8 +212,8 @@ void Hand::render(bool isMine) {
}
}
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayHands)) {
renderLeapHands(isMine);
if (renderMode != Model::SHADOW_RENDER_MODE && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHands)) {
renderHandTargets(isMine);
}
glEnable(GL_DEPTH_TEST);
@ -221,11 +221,11 @@ void Hand::render(bool isMine) {
}
void Hand::renderLeapHands(bool isMine) {
void Hand::renderHandTargets(bool isMine) {
const float alpha = 1.0f;
const glm::vec3 handColor(1.0, 0.84, 0.66); // use the skin color
const glm::vec3 handColor(1.0, 0.0, 0.0); // Color the hand targets red to be different than skin
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
@ -262,23 +262,37 @@ void Hand::renderLeapHands(bool isMine) {
glPopMatrix();
}
}
// Draw the finger root cones
const float PALM_BALL_RADIUS = 0.03f;
const float PALM_DISK_RADIUS = 0.06f;
const float PALM_DISK_THICKNESS = 0.01f;
const float PALM_FINGER_ROD_RADIUS = 0.003f;
// Draw the palm ball and disk
for (size_t i = 0; i < getNumPalms(); ++i) {
PalmData& palm = getPalms()[i];
if (palm.isActive()) {
for (size_t f = 0; f < palm.getNumFingers(); ++f) {
FingerData& finger = palm.getFingers()[f];
if (finger.isActive()) {
glColor4f(handColor.r, handColor.g, handColor.b, 0.5);
glColor4f(handColor.r, handColor.g, handColor.b, alpha);
glm::vec3 tip = finger.getTipPosition();
glm::vec3 root = finger.getRootPosition();
Avatar::renderJointConnectingCone(root, tip, 0.001f, 0.003f);
Avatar::renderJointConnectingCone(root, tip, PALM_FINGER_ROD_RADIUS, PALM_FINGER_ROD_RADIUS);
// Render sphere at palm/finger root
glm::vec3 palmNormal = root + palm.getNormal() * PALM_DISK_THICKNESS;
Avatar::renderJointConnectingCone(root, palmNormal, PALM_DISK_RADIUS, 0.0f);
glPushMatrix();
glTranslatef(root.x, root.y, root.z);
glutSolidSphere(PALM_BALL_RADIUS, 20.0f, 20.0f);
glPopMatrix();
}
}
}
}
/*
// Draw the hand paddles
int MAX_NUM_PADDLES = 2; // one for left and one for right
glColor4f(handColor.r, handColor.g, handColor.b, 0.3f);
@ -309,6 +323,7 @@ void Hand::renderLeapHands(bool isMine) {
Avatar::renderJointConnectingCone(root, tip, HAND_PADDLE_RADIUS, 0.f);
}
}
*/
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);

View file

@ -24,6 +24,7 @@
#include <HandData.h>
#include "InterfaceConfig.h"
#include "renderer/Model.h"
#include "world.h"
@ -52,7 +53,7 @@ public:
void init();
void reset();
void simulate(float deltaTime, bool isMine);
void render(bool isMine);
void render(bool isMine, Model::RenderMode renderMode = Model::DEFAULT_RENDER_MODE);
// getters
const glm::vec3& getLeapFingerTipBallPosition (int ball) const { return _leapFingerTipBalls [ball].position;}
@ -75,7 +76,7 @@ private:
std::vector<HandBall> _leapFingerTipBalls;
std::vector<HandBall> _leapFingerRootBalls;
void renderLeapHands(bool isMine);
void renderHandTargets(bool isMine);
void renderLeapFingerTrails();
void calculateGeometry();

View file

@ -106,16 +106,10 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
const float BROW_LIFT_THRESHOLD = 100.0f;
if (_audioAttack > BROW_LIFT_THRESHOLD) {
_browAudioLift += sqrtf(_audioAttack) * 0.00005f;
_browAudioLift += sqrtf(_audioAttack) * 0.01f;
}
_browAudioLift = glm::clamp(_browAudioLift *= 0.7f, 0.0f, 1.0f);
const float CLAMP = 0.01f;
if (_browAudioLift > CLAMP) {
_browAudioLift = CLAMP;
}
_browAudioLift *= 0.7f;
const float BLINK_SPEED = 10.0f;
const float FULLY_OPEN = 0.0f;
const float FULLY_CLOSED = 1.0f;
@ -147,12 +141,12 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
}
// use data to update fake Faceshift blendshape coefficients
const float BROW_LIFT_SCALE = 500.0f;
const float JAW_OPEN_SCALE = 0.01f;
const float JAW_OPEN_DEAD_ZONE = 0.75f;
Application::getInstance()->getFaceshift()->updateFakeCoefficients(_leftEyeBlink, _rightEyeBlink,
min(1.0f, _browAudioLift * BROW_LIFT_SCALE), glm::clamp(sqrt(_averageLoudness * JAW_OPEN_SCALE) -
JAW_OPEN_DEAD_ZONE, 0.0f, 1.0f), _blendshapeCoefficients);
const float JAW_OPEN_SCALE = 10.f;
Application::getInstance()->getFaceshift()->updateFakeCoefficients(_leftEyeBlink,
_rightEyeBlink,
_browAudioLift,
glm::clamp(log(_averageLoudness) / JAW_OPEN_SCALE, 0.0f, 1.0f),
_blendshapeCoefficients);
}
if (!isMine) {
@ -182,7 +176,7 @@ void Head::relaxLean(float deltaTime) {
}
void Head::render(float alpha, Model::RenderMode mode) {
if (_faceModel.render(alpha, mode) && _renderLookatVectors) {
if (_faceModel.render(alpha, mode) && _renderLookatVectors && mode != Model::SHADOW_RENDER_MODE) {
renderLookatVectors(_leftEyePosition, _rightEyePosition, _lookAtPosition);
}
}

View file

@ -102,13 +102,17 @@ void MyAvatar::reset() {
setVelocity(glm::vec3(0.0f));
setThrust(glm::vec3(0.0f));
setOrientation(glm::quat(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;
setOrientation(glm::quat(eulers));
}
void MyAvatar::update(float deltaTime) {
Head* head = getHead();
head->relaxLean(deltaTime);
updateFromGyros(deltaTime);
updateFromFaceTracker(deltaTime);
if (Menu::getInstance()->isOptionChecked(MenuOption::MoveWithLean)) {
// Faceshift drive is enabled, set the avatar drive based on the head position
moveWithLean();
@ -135,7 +139,14 @@ void MyAvatar::simulate(float deltaTime) {
}
// update the movement of the hand and process handshaking with other avatars...
updateHandMovementAndTouching(deltaTime);
bool pointing = false;
if (_mousePressed) {
_handState = HAND_STATE_GRASPING;
} else if (pointing) {
_handState = HAND_STATE_POINTING;
} else {
_handState = HAND_STATE_NULL;
}
updateOrientation(deltaTime);
@ -231,7 +242,7 @@ void MyAvatar::simulate(float deltaTime) {
}
// Update avatar head rotation with sensor data
void MyAvatar::updateFromGyros(float deltaTime) {
void MyAvatar::updateFromFaceTracker(float deltaTime) {
glm::vec3 estimatedPosition, estimatedRotation;
FaceTracker* tracker = Application::getInstance()->getActiveFaceTracker();
@ -340,7 +351,9 @@ void MyAvatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) {
return; // exit early
}
Avatar::render(cameraPosition, renderMode);
if (Menu::getInstance()->isOptionChecked(MenuOption::ShowIKConstraints)) {
// don't display IK constraints in shadow mode
if (Menu::getInstance()->isOptionChecked(MenuOption::ShowIKConstraints) && renderMode != SHADOW_RENDER_MODE) {
_skeletonModel.renderIKConstraints();
}
}
@ -589,7 +602,7 @@ void MyAvatar::renderBody(RenderMode renderMode, float glowLevel) {
if (shouldRenderHead(Application::getInstance()->getCamera()->getPosition(), renderMode)) {
getHead()->render(1.0f, modelRenderMode);
}
getHand()->render(true);
getHand()->render(true, modelRenderMode);
}
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.50f;
@ -910,40 +923,6 @@ void MyAvatar::updateThrust(float deltaTime) {
}
*/
void MyAvatar::updateHandMovementAndTouching(float deltaTime) {
glm::quat orientation = getOrientation();
// reset hand and arm positions according to hand movement
glm::vec3 up = orientation * IDENTITY_UP;
bool pointing = false;
if (glm::length(_mouseRayDirection) > EPSILON && !Application::getInstance()->isMouseHidden()) {
// confine to the approximate shoulder plane
glm::vec3 pointDirection = _mouseRayDirection;
if (glm::dot(_mouseRayDirection, up) > 0.0f) {
glm::vec3 projectedVector = glm::cross(up, glm::cross(_mouseRayDirection, up));
if (glm::length(projectedVector) > EPSILON) {
pointDirection = glm::normalize(projectedVector);
}
}
glm::vec3 shoulderPosition;
if (_skeletonModel.getRightShoulderPosition(shoulderPosition)) {
glm::vec3 farVector = _mouseRayOrigin + pointDirection * (float)TREE_SCALE - shoulderPosition;
const float ARM_RETRACTION = 0.75f;
float retractedLength = _skeletonModel.getRightArmLength() * ARM_RETRACTION;
setHandPosition(shoulderPosition + glm::normalize(farVector) * retractedLength);
pointing = true;
}
}
if (_mousePressed) {
_handState = HAND_STATE_GRASPING;
} else if (pointing) {
_handState = HAND_STATE_POINTING;
} else {
_handState = HAND_STATE_NULL;
}
}
void MyAvatar::updateCollisionWithEnvironment(float deltaTime, float radius) {
glm::vec3 up = getBodyUpDirection();
@ -1424,6 +1403,8 @@ void MyAvatar::updateMotionBehaviorsFromMenu() {
_motionBehaviors |= AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY;
// Environmental and Local gravities are incompatible. Environmental setting trumps local.
_motionBehaviors &= ~AVATAR_MOTION_OBEY_LOCAL_GRAVITY;
} else {
_motionBehaviors &= ~AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY;
}
if (! (_motionBehaviors & (AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY | AVATAR_MOTION_OBEY_LOCAL_GRAVITY))) {
setGravity(glm::vec3(0.0f));

View file

@ -38,7 +38,7 @@ public:
void reset();
void update(float deltaTime);
void simulate(float deltaTime);
void updateFromGyros(float deltaTime);
void updateFromFaceTracker(float deltaTime);
void moveWithLean();
void render(const glm::vec3& cameraPosition, RenderMode renderMode = NORMAL_RENDER_MODE);
@ -147,7 +147,6 @@ private:
float computeMotorTimescale();
void applyMotor(float deltaTime);
void applyThrust(float deltaTime);
void updateHandMovementAndTouching(float deltaTime);
void updateCollisionWithAvatars(float deltaTime);
void updateCollisionWithEnvironment(float deltaTime, float radius);
void updateCollisionWithVoxels(float deltaTime, float radius);

View file

@ -38,24 +38,23 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
Hand* hand = _owningAvatar->getHand();
hand->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex);
const float HAND_RESTORATION_PERIOD = 1.f; // seconds
float handRestorePercent = glm::clamp(deltaTime / HAND_RESTORATION_PERIOD, 0.f, 1.f);
const float HAND_RESTORATION_RATE = 0.25f;
const FBXGeometry& geometry = _geometry->getFBXGeometry();
if (leftPalmIndex == -1) {
// no Leap data; set hands from mouse
if (_owningAvatar->getHandState() == HAND_STATE_NULL) {
restoreRightHandPosition(handRestorePercent);
restoreRightHandPosition(HAND_RESTORATION_RATE);
} else {
applyHandPosition(geometry.rightHandJointIndex, _owningAvatar->getHandPosition());
}
restoreLeftHandPosition(handRestorePercent);
restoreLeftHandPosition(HAND_RESTORATION_RATE);
} else if (leftPalmIndex == rightPalmIndex) {
// right hand only
applyPalmData(geometry.rightHandJointIndex, geometry.rightFingerJointIndices, geometry.rightFingertipJointIndices,
hand->getPalms()[leftPalmIndex]);
restoreLeftHandPosition(handRestorePercent);
restoreLeftHandPosition(HAND_RESTORATION_RATE);
} else {
applyPalmData(geometry.leftHandJointIndex, geometry.leftFingerJointIndices, geometry.leftFingertipJointIndices,

View file

@ -220,15 +220,10 @@ void FaceplusReader::update() {
if (!_referenceInitialized) {
_referenceX = x;
_referenceY = y;
_referenceScale = scale;
_referenceInitialized = true;
}
const float TRANSLATION_SCALE = 10.0f;
const float REFERENCE_DISTANCE = 10.0f;
float depthScale = _referenceScale / scale;
float z = REFERENCE_DISTANCE * (depthScale - 1.0f);
glm::vec3 headTranslation((x - _referenceX) * depthScale * TRANSLATION_SCALE,
(y - _referenceY) * depthScale * TRANSLATION_SCALE, z);
glm::vec3 headTranslation((x - _referenceX) * TRANSLATION_SCALE, (y - _referenceY) * TRANSLATION_SCALE, 0.0f);
glm::quat headRotation(glm::radians(glm::vec3(-_outputVector.at(_headRotationIndices[0]),
_outputVector.at(_headRotationIndices[1]), -_outputVector.at(_headRotationIndices[2]))));
float estimatedEyePitch = (_outputVector.at(_leftEyeRotationIndices[0]) +

View file

@ -76,7 +76,6 @@ private:
int _rightEyeRotationIndices[2];
float _referenceX;
float _referenceY;
float _referenceScale;
bool _referenceInitialized;
QVector<float> _blendshapeCoefficients;
#endif

View file

@ -30,6 +30,7 @@ const float NECK_Z = 300.f; // millimeters
SixenseManager::SixenseManager() {
#ifdef HAVE_SIXENSE
_lastMovement = 0;
_amountMoved = glm::vec3(0.0f);
_calibrationState = CALIBRATION_STATE_IDLE;
// By default we assume the _neckBase (in orb frame) is as high above the orb
@ -122,14 +123,21 @@ void SixenseManager::update(float deltaTime) {
palm->setRawRotation(rotation);
// Compute current velocity from position change
glm::vec3 rawVelocity = (position - palm->getRawPosition()) / deltaTime / 1000.f;
glm::vec3 rawVelocity;
if (deltaTime > 0.f) {
rawVelocity = (position - palm->getRawPosition()) / deltaTime / 1000.f;
} else {
rawVelocity = glm::vec3(0.0f);
}
palm->setRawVelocity(rawVelocity); // meters/sec
palm->setRawPosition(position);
// use the velocity to determine whether there's any movement (if the hand isn't new)
const float MOVEMENT_SPEED_THRESHOLD = 0.05f;
if (glm::length(rawVelocity) > MOVEMENT_SPEED_THRESHOLD && foundHand) {
const float MOVEMENT_DISTANCE_THRESHOLD = 0.003f;
_amountMoved += rawVelocity * deltaTime;
if (glm::length(_amountMoved) > MOVEMENT_DISTANCE_THRESHOLD && foundHand) {
_lastMovement = usecTimestampNow();
_amountMoved = glm::vec3(0.0f);
}
// initialize the "finger" based on the direction
@ -143,7 +151,11 @@ void SixenseManager::update(float deltaTime) {
// Store the one fingertip in the palm structure so we can track velocity
glm::vec3 oldTipPosition = palm->getTipRawPosition();
palm->setTipVelocity((newTipPosition - oldTipPosition) / deltaTime / 1000.f);
if (deltaTime > 0.f) {
palm->setTipVelocity((newTipPosition - oldTipPosition) / deltaTime / 1000.f);
} else {
palm->setTipVelocity(glm::vec3(0.f));
}
palm->setTipPosition(newTipPosition);
// three fingers indicates to the skeleton that we have enough data to determine direction
@ -158,8 +170,8 @@ void SixenseManager::update(float deltaTime) {
}
// if the controllers haven't been moved in a while, disable
const unsigned int MOVEMENT_DISABLE_DURATION = 30 * 1000 * 1000;
if (usecTimestampNow() - _lastMovement > MOVEMENT_DISABLE_DURATION) {
const unsigned int MOVEMENT_DISABLE_SECONDS = 3;
if (usecTimestampNow() - _lastMovement > (MOVEMENT_DISABLE_SECONDS * 1000 * 1000)) {
for (std::vector<PalmData>::iterator it = hand->getPalms().begin(); it != hand->getPalms().end(); it++) {
it->setActive(false);
}

View file

@ -64,6 +64,7 @@ private:
#endif
quint64 _lastMovement;
glm::vec3 _amountMoved;
};
#endif // hifi_SixenseManager_h

View file

@ -39,8 +39,8 @@ void ModelTreeRenderer::update() {
}
}
void ModelTreeRenderer::render() {
OctreeRenderer::render();
void ModelTreeRenderer::render(RenderMode renderMode) {
OctreeRenderer::render(renderMode);
}
Model* ModelTreeRenderer::getModel(const QString& url) {
@ -66,54 +66,130 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
const QList<ModelItem>& modelItems = modelTreeElement->getModels();
uint16_t numberOfModels = modelItems.size();
bool isShadowMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE;
bool displayModelBounds = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelBounds);
bool displayElementProxy = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelElementProxy);
bool displayElementChildProxies = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelElementChildProxies);
if (!isShadowMode && displayElementProxy && numberOfModels > 0) {
glm::vec3 elementCenter = modelTreeElement->getAABox().calcCenter() * (float)TREE_SCALE;
float elementSize = modelTreeElement->getScale() * (float)TREE_SCALE;
glColor3f(1.0f, 0.0f, 0.0f);
glPushMatrix();
glTranslatef(elementCenter.x, elementCenter.y, elementCenter.z);
glutWireCube(elementSize);
glPopMatrix();
if (displayElementChildProxies) {
// draw the children
float halfSize = elementSize / 2.0f;
float quarterSize = elementSize / 4.0f;
glColor3f(1.0f, 1.0f, 0.0f);
glPushMatrix();
glTranslatef(elementCenter.x - quarterSize, elementCenter.y - quarterSize, elementCenter.z - quarterSize);
glutWireCube(halfSize);
glPopMatrix();
glColor3f(1.0f, 0.0f, 1.0f);
glPushMatrix();
glTranslatef(elementCenter.x + quarterSize, elementCenter.y - quarterSize, elementCenter.z - quarterSize);
glutWireCube(halfSize);
glPopMatrix();
glColor3f(0.0f, 1.0f, 0.0f);
glPushMatrix();
glTranslatef(elementCenter.x - quarterSize, elementCenter.y + quarterSize, elementCenter.z - quarterSize);
glutWireCube(halfSize);
glPopMatrix();
glColor3f(0.0f, 0.0f, 1.0f);
glPushMatrix();
glTranslatef(elementCenter.x - quarterSize, elementCenter.y - quarterSize, elementCenter.z + quarterSize);
glutWireCube(halfSize);
glPopMatrix();
glColor3f(1.0f, 1.0f, 1.0f);
glPushMatrix();
glTranslatef(elementCenter.x + quarterSize, elementCenter.y + quarterSize, elementCenter.z + quarterSize);
glutWireCube(halfSize);
glPopMatrix();
glColor3f(0.0f, 0.5f, 0.5f);
glPushMatrix();
glTranslatef(elementCenter.x - quarterSize, elementCenter.y + quarterSize, elementCenter.z + quarterSize);
glutWireCube(halfSize);
glPopMatrix();
glColor3f(0.5f, 0.0f, 0.0f);
glPushMatrix();
glTranslatef(elementCenter.x + quarterSize, elementCenter.y - quarterSize, elementCenter.z + quarterSize);
glutWireCube(halfSize);
glPopMatrix();
glColor3f(0.0f, 0.5f, 0.0f);
glPushMatrix();
glTranslatef(elementCenter.x + quarterSize, elementCenter.y + quarterSize, elementCenter.z - quarterSize);
glutWireCube(halfSize);
glPopMatrix();
}
}
for (uint16_t i = 0; i < numberOfModels; i++) {
const ModelItem& modelItem = modelItems[i];
// render modelItem aspoints
glm::vec3 position = modelItem.getPosition() * (float)TREE_SCALE;
glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]);
float radius = modelItem.getRadius() * (float)TREE_SCALE;
//glm::vec3 center = position + glm::vec3(radius, radius, radius); // center it around the position
AABox modelBox = modelItem.getAABox();
modelBox.scale(TREE_SCALE);
if (args->_viewFrustum->boxInFrustum(modelBox) != ViewFrustum::OUTSIDE) {
glm::vec3 position = modelItem.getPosition() * (float)TREE_SCALE;
float radius = modelItem.getRadius() * (float)TREE_SCALE;
float size = modelItem.getSize() * (float)TREE_SCALE;
bool drawAsModel = modelItem.hasModel();
bool drawAsModel = modelItem.hasModel();
args->_renderedItems++;
args->_renderedItems++;
if (drawAsModel) {
glPushMatrix();
const float alpha = 1.0f;
if (drawAsModel) {
glPushMatrix();
const float alpha = 1.0f;
Model* model = getModel(modelItem.getModelURL());
Model* model = getModel(modelItem.getModelURL());
model->setScaleToFit(true, radius * 2.0f);
model->setSnapModelToCenter(true);
model->setScaleToFit(true, radius * 2.0f);
model->setSnapModelToCenter(true);
// set the rotation
glm::quat rotation = modelItem.getModelRotation();
model->setRotation(rotation);
// set the rotation
glm::quat rotation = modelItem.getModelRotation();
model->setRotation(rotation);
// set the position
model->setTranslation(position);
// set the position
model->setTranslation(position);
model->simulate(0.0f);
model->simulate(0.0f);
// TODO: should we allow modelItems to have alpha on their models?
Model::RenderMode modelRenderMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE
? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
model->render(alpha, modelRenderMode);
if (!isShadowMode && displayModelBounds) {
glColor3f(0.0f, 1.0f, 0.0f);
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glutWireCube(size);
glPopMatrix();
}
model->render(alpha); // TODO: should we allow modelItems to have alpha on their models?
const bool wantDebugSphere = false;
if (wantDebugSphere) {
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glutWireSphere(radius, 15, 15);
glPopMatrix();
}
glPopMatrix();
} else {
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glutSolidSphere(radius, 15, 15);
glPopMatrix();
glPopMatrix();
} else {
glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]);
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glutSolidSphere(radius, 15, 15);
glPopMatrix();
}
}
}
}

View file

@ -46,7 +46,7 @@ public:
void processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode);
virtual void init();
virtual void render();
virtual void render(RenderMode renderMode = DEFAULT_RENDER_MODE);
protected:
Model* getModel(const QString& url);

View file

@ -39,8 +39,8 @@ void ParticleTreeRenderer::update() {
}
}
void ParticleTreeRenderer::render() {
OctreeRenderer::render();
void ParticleTreeRenderer::render(RenderMode renderMode) {
OctreeRenderer::render(renderMode);
}
Model* ParticleTreeRenderer::getModel(const QString& url) {
@ -102,7 +102,11 @@ void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* arg
model->setScale(scale * MODEL_SCALE * radius * modelScale);
model->simulate(0.0f);
model->render(alpha); // TODO: should we allow particles to have alpha on their models?
// TODO: should we allow particles to have alpha on their models?
Model::RenderMode modelRenderMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE
? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
model->render(alpha, modelRenderMode);
const bool wantDebugSphere = false;
if (wantDebugSphere) {

View file

@ -43,7 +43,7 @@ public:
void processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode);
virtual void init();
virtual void render();
virtual void render(RenderMode renderMode = DEFAULT_RENDER_MODE);
protected:
Model* getModel(const QString& url);

View file

@ -342,7 +342,8 @@ bool NetworkGeometry::isLoadedWithTextures() const {
foreach (const NetworkMesh& mesh, _meshes) {
foreach (const NetworkMeshPart& part, mesh.parts) {
if ((part.diffuseTexture && !part.diffuseTexture->isLoaded()) ||
(part.normalTexture && !part.normalTexture->isLoaded())) {
(part.normalTexture && !part.normalTexture->isLoaded()) ||
(part.specularTexture && !part.specularTexture->isLoaded())) {
return false;
}
}
@ -416,6 +417,9 @@ void NetworkGeometry::setLoadPriority(const QPointer<QObject>& owner, float prio
if (part.normalTexture) {
part.normalTexture->setLoadPriority(owner, priority);
}
if (part.specularTexture) {
part.specularTexture->setLoadPriority(owner, priority);
}
}
}
}
@ -433,6 +437,9 @@ void NetworkGeometry::setLoadPriorities(const QHash<QPointer<QObject>, float>& p
if (part.normalTexture) {
part.normalTexture->setLoadPriorities(priorities);
}
if (part.specularTexture) {
part.specularTexture->setLoadPriorities(priorities);
}
}
}
}
@ -450,6 +457,9 @@ void NetworkGeometry::clearLoadPriority(const QPointer<QObject>& owner) {
if (part.normalTexture) {
part.normalTexture->clearLoadPriority(owner);
}
if (part.specularTexture) {
part.specularTexture->clearLoadPriority(owner);
}
}
}
}
@ -566,6 +576,11 @@ void NetworkGeometry::setGeometry(const FBXGeometry& geometry) {
_textureBase.resolved(QUrl(part.normalTexture.filename)), true, false, part.normalTexture.content);
networkPart.normalTexture->setLoadPriorities(_loadPriorities);
}
if (!part.specularTexture.filename.isEmpty()) {
networkPart.specularTexture = Application::getInstance()->getTextureCache()->getTexture(
_textureBase.resolved(QUrl(part.specularTexture.filename)), true, false, part.specularTexture.content);
networkPart.specularTexture->setLoadPriorities(_loadPriorities);
}
networkMesh.parts.append(networkPart);
totalIndices += (part.quadIndices.size() + part.triangleIndices.size());

View file

@ -124,6 +124,7 @@ public:
QSharedPointer<NetworkTexture> diffuseTexture;
QSharedPointer<NetworkTexture> normalTexture;
QSharedPointer<NetworkTexture> specularTexture;
bool isTranslucent() const;
};

View file

@ -56,13 +56,20 @@ Model::~Model() {
ProgramObject Model::_program;
ProgramObject Model::_normalMapProgram;
ProgramObject Model::_specularMapProgram;
ProgramObject Model::_normalSpecularMapProgram;
ProgramObject Model::_shadowProgram;
ProgramObject Model::_skinProgram;
ProgramObject Model::_skinNormalMapProgram;
ProgramObject Model::_skinSpecularMapProgram;
ProgramObject Model::_skinNormalSpecularMapProgram;
ProgramObject Model::_skinShadowProgram;
int Model::_normalMapTangentLocation;
int Model::_normalSpecularMapTangentLocation;
Model::SkinLocations Model::_skinLocations;
Model::SkinLocations Model::_skinNormalMapLocations;
Model::SkinLocations Model::_skinSpecularMapLocations;
Model::SkinLocations Model::_skinNormalSpecularMapLocations;
Model::SkinLocations Model::_skinShadowLocations;
void Model::setScale(const glm::vec3& scale) {
@ -92,7 +99,7 @@ void Model::setOffset(const glm::vec3& offset) {
}
void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locations) {
void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locations, int specularTextureUnit) {
program.bind();
locations.clusterMatrices = program.uniformLocation("clusterMatrices");
locations.clusterIndices = program.attributeLocation("clusterIndices");
@ -100,6 +107,7 @@ void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locati
locations.tangent = program.attributeLocation("tangent");
program.setUniformValue("diffuseMap", 0);
program.setUniformValue("normalMap", 1);
program.setUniformValue("specularMap", specularTextureUnit);
program.release();
}
@ -162,10 +170,10 @@ void Model::init() {
_program.setUniformValue("texture", 0);
_program.release();
_normalMapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath()
+ "shaders/model_normal_map.vert");
_normalMapProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath()
+ "shaders/model_normal_map.frag");
_normalMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
Application::resourcesPath() + "shaders/model_normal_map.vert");
_normalMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/model_normal_map.frag");
_normalMapProgram.link();
_normalMapProgram.bind();
@ -174,27 +182,65 @@ void Model::init() {
_normalMapTangentLocation = _normalMapProgram.attributeLocation("tangent");
_normalMapProgram.release();
_specularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
Application::resourcesPath() + "shaders/model.vert");
_specularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/model_specular_map.frag");
_specularMapProgram.link();
_specularMapProgram.bind();
_specularMapProgram.setUniformValue("diffuseMap", 0);
_specularMapProgram.setUniformValue("specularMap", 1);
_specularMapProgram.release();
_normalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
Application::resourcesPath() + "shaders/model_normal_map.vert");
_normalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/model_normal_specular_map.frag");
_normalSpecularMapProgram.link();
_normalSpecularMapProgram.bind();
_normalSpecularMapProgram.setUniformValue("diffuseMap", 0);
_normalSpecularMapProgram.setUniformValue("normalMap", 1);
_normalSpecularMapProgram.setUniformValue("specularMap", 2);
_normalSpecularMapTangentLocation = _normalMapProgram.attributeLocation("tangent");
_normalSpecularMapProgram.release();
_shadowProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model_shadow.vert");
_shadowProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() +
"shaders/model_shadow.frag");
_shadowProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/model_shadow.frag");
_shadowProgram.link();
_skinProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath()
+ "shaders/skin_model.vert");
_skinProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath()
+ "shaders/model.frag");
_skinProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/skin_model.vert");
_skinProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + "shaders/model.frag");
_skinProgram.link();
initSkinProgram(_skinProgram, _skinLocations);
_skinNormalMapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath()
+ "shaders/skin_model_normal_map.vert");
_skinNormalMapProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath()
+ "shaders/model_normal_map.frag");
_skinNormalMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
Application::resourcesPath() + "shaders/skin_model_normal_map.vert");
_skinNormalMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/model_normal_map.frag");
_skinNormalMapProgram.link();
initSkinProgram(_skinNormalMapProgram, _skinNormalMapLocations);
_skinSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
Application::resourcesPath() + "shaders/skin_model.vert");
_skinSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/model_specular_map.frag");
_skinSpecularMapProgram.link();
initSkinProgram(_skinSpecularMapProgram, _skinSpecularMapLocations);
_skinNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
Application::resourcesPath() + "shaders/skin_model_normal_map.vert");
_skinNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/model_normal_specular_map.frag");
_skinNormalSpecularMapProgram.link();
initSkinProgram(_skinNormalSpecularMapProgram, _skinNormalSpecularMapLocations, 2);
_skinShadowProgram.addShaderFromSourceFile(QGLShader::Vertex,
Application::resourcesPath() + "shaders/skin_model_shadow.vert");
_skinShadowProgram.addShaderFromSourceFile(QGLShader::Fragment,
@ -1331,15 +1377,29 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) {
ProgramObject* program = &_program;
ProgramObject* skinProgram = &_skinProgram;
SkinLocations* skinLocations = &_skinLocations;
GLenum specularTextureUnit = 0;
if (mode == SHADOW_RENDER_MODE) {
program = &_shadowProgram;
skinProgram = &_skinShadowProgram;
skinLocations = &_skinShadowLocations;
} else if (!mesh.tangents.isEmpty()) {
program = &_normalMapProgram;
skinProgram = &_skinNormalMapProgram;
skinLocations = &_skinNormalMapLocations;
if (mesh.hasSpecularTexture()) {
program = &_normalSpecularMapProgram;
skinProgram = &_skinNormalSpecularMapProgram;
skinLocations = &_skinNormalSpecularMapLocations;
specularTextureUnit = GL_TEXTURE2;
} else {
program = &_normalMapProgram;
skinProgram = &_skinNormalMapProgram;
skinLocations = &_skinNormalMapLocations;
}
} else if (mesh.hasSpecularTexture()) {
program = &_specularMapProgram;
skinProgram = &_skinSpecularMapProgram;
skinLocations = &_skinSpecularMapLocations;
specularTextureUnit = GL_TEXTURE1;
}
const MeshState& state = _meshStates.at(i);
@ -1427,13 +1487,23 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) {
glBindTexture(GL_TEXTURE_2D, !diffuseMap ?
Application::getInstance()->getTextureCache()->getWhiteTextureID() : diffuseMap->getID());
if (!mesh.tangents.isEmpty()) {
specularTextureUnit = GL_TEXTURE2;
glActiveTexture(GL_TEXTURE1);
Texture* normalMap = networkPart.normalTexture.data();
glBindTexture(GL_TEXTURE_2D, !normalMap ?
Application::getInstance()->getTextureCache()->getBlueTextureID() : normalMap->getID());
glActiveTexture(GL_TEXTURE0);
}
if (specularTextureUnit) {
glActiveTexture(specularTextureUnit);
Texture* specularMap = networkPart.specularTexture.data();
glBindTexture(GL_TEXTURE_2D, !specularMap ?
Application::getInstance()->getTextureCache()->getWhiteTextureID() : specularMap->getID());
glActiveTexture(GL_TEXTURE0);
}
}
glDrawRangeElementsEXT(GL_QUADS, 0, vertexCount - 1, part.quadIndices.size(), GL_UNSIGNED_INT, (void*)offset);
offset += part.quadIndices.size() * sizeof(int);
@ -1456,7 +1526,13 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) {
activeProgram->disableAttributeArray(tangentLocation);
}
if (specularTextureUnit) {
glActiveTexture(specularTextureUnit);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0);
}
if (state.clusterMatrices.size() > 1) {
skinProgram->disableAttributeArray(skinLocations->clusterIndices);
skinProgram->disableAttributeArray(skinLocations->clusterWeights);

View file

@ -318,12 +318,17 @@ private:
static ProgramObject _program;
static ProgramObject _normalMapProgram;
static ProgramObject _specularMapProgram;
static ProgramObject _normalSpecularMapProgram;
static ProgramObject _shadowProgram;
static ProgramObject _skinProgram;
static ProgramObject _skinNormalMapProgram;
static ProgramObject _skinSpecularMapProgram;
static ProgramObject _skinNormalSpecularMapProgram;
static ProgramObject _skinShadowProgram;
static int _normalMapTangentLocation;
static int _normalSpecularMapTangentLocation;
class SkinLocations {
public:
@ -335,9 +340,11 @@ private:
static SkinLocations _skinLocations;
static SkinLocations _skinNormalMapLocations;
static SkinLocations _skinSpecularMapLocations;
static SkinLocations _skinNormalSpecularMapLocations;
static SkinLocations _skinShadowLocations;
static void initSkinProgram(ProgramObject& program, SkinLocations& locations);
static void initSkinProgram(ProgramObject& program, SkinLocations& locations, int specularTextureUnit = 1);
};
Q_DECLARE_METATYPE(QPointer<Model>)

View file

@ -37,6 +37,7 @@ AttachmentsDialog::AttachmentsDialog() :
container->setLayout(_attachments = new QVBoxLayout());
container->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
area->setWidget(container);
_attachments->addStretch(1);
foreach (const AttachmentData& data, Application::getInstance()->getAvatar()->getAttachmentData()) {
addAttachment(data);
@ -49,20 +50,30 @@ AttachmentsDialog::AttachmentsDialog() :
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok);
layout->addWidget(buttons);
connect(buttons, SIGNAL(accepted()), SLOT(deleteLater()));
_ok = buttons->button(QDialogButtonBox::Ok);
setMinimumSize(600, 600);
}
void AttachmentsDialog::setVisible(bool visible) {
QDialog::setVisible(visible);
// un-default the OK button
if (visible) {
_ok->setDefault(false);
}
}
void AttachmentsDialog::updateAttachmentData() {
QVector<AttachmentData> data;
for (int i = 0; i < _attachments->count(); i++) {
for (int i = 0; i < _attachments->count() - 1; i++) {
data.append(static_cast<AttachmentPanel*>(_attachments->itemAt(i)->widget())->getAttachmentData());
}
Application::getInstance()->getAvatar()->setAttachmentData(data);
}
void AttachmentsDialog::addAttachment(const AttachmentData& data) {
_attachments->addWidget(new AttachmentPanel(this, data));
_attachments->insertWidget(_attachments->count() - 1, new AttachmentPanel(this, data));
}
static QDoubleSpinBox* createTranslationBox(AttachmentsDialog* dialog, float value) {
@ -86,7 +97,10 @@ static QDoubleSpinBox* createRotationBox(AttachmentsDialog* dialog, float value)
}
AttachmentPanel::AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData& data) {
setFrameStyle(QFrame::StyledPanel);
QFormLayout* layout = new QFormLayout();
layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
setLayout(layout);
QHBoxLayout* urlBox = new QHBoxLayout();
@ -130,6 +144,7 @@ AttachmentPanel::AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData
QPushButton* remove = new QPushButton("Delete");
layout->addRow(remove);
connect(remove, SIGNAL(clicked(bool)), SLOT(deleteLater()));
dialog->connect(remove, SIGNAL(clicked(bool)), SLOT(updateAttachmentData()), Qt::QueuedConnection);
}
AttachmentData AttachmentPanel::getAttachmentData() const {

View file

@ -13,6 +13,7 @@
#define hifi_AttachmentsDialog_h
#include <QDialog>
#include <QFrame>
#include <AvatarData.h>
@ -29,6 +30,8 @@ public:
AttachmentsDialog();
virtual void setVisible(bool visible);
public slots:
void updateAttachmentData();
@ -40,10 +43,11 @@ private slots:
private:
QVBoxLayout* _attachments;
QPushButton* _ok;
};
/// A panel controlling a single attachment.
class AttachmentPanel : public QWidget {
class AttachmentPanel : public QFrame {
Q_OBJECT
public:

View file

@ -1711,38 +1711,6 @@ bool VoxelSystem::inspectForExteriorOcclusionsOperation(OctreeElement* element,
return true;
}
void VoxelSystem::cullSharedFaces() {
if (Menu::getInstance()->isOptionChecked(MenuOption::CullSharedFaces)) {
_useVoxelShader = false;
_usePrimitiveRenderer = true;
inspectForOcclusions();
} else {
_usePrimitiveRenderer = false;
clearAllNodesBufferIndex();
}
_writeRenderFullVBO = true;
_tree->setDirtyBit();
setupNewVoxelsForDrawing();
}
void VoxelSystem::showCulledSharedFaces() {
_tree->lockForRead();
if (Menu::getInstance()->isOptionChecked(MenuOption::ShowCulledSharedFaces)) {
_showCulledSharedFaces = true;
} else {
_showCulledSharedFaces = false;
}
_tree->unlock();
if (Menu::getInstance()->isOptionChecked(MenuOption::CullSharedFaces)) {
_writeRenderFullVBO = true;
_tree->setDirtyBit();
setupNewVoxelsForDrawing();
}
}
void VoxelSystem::inspectForOcclusions() {
if (_inOcclusions) {
@ -2032,7 +2000,7 @@ bool VoxelSystem::hideOutOfViewOperation(OctreeElement* element, void* extraData
// if this node is fully OUTSIDE the view, but previously intersected and/or was inside the last view, then
// we need to hide it. Additionally we know that ALL of it's children are also fully OUTSIDE so we can recurse
// the children and simply mark them as hidden
args->tree->recurseNodeWithOperation(voxel, hideAllSubTreeOperation, args );
args->tree->recurseElementWithOperation(voxel, hideAllSubTreeOperation, args );
return false;
} break;
@ -2049,7 +2017,7 @@ bool VoxelSystem::hideOutOfViewOperation(OctreeElement* element, void* extraData
// if this node is fully INSIDE the view, but previously INTERSECTED and/or was OUTSIDE the last view, then
// we need to show it. Additionally we know that ALL of it's children are also fully INSIDE so we can recurse
// the children and simply mark them as visible (as appropriate based on LOD)
args->tree->recurseNodeWithOperation(voxel, showAllSubTreeOperation, args);
args->tree->recurseElementWithOperation(voxel, showAllSubTreeOperation, args);
return false;
} break;
case ViewFrustum::INTERSECT: {

View file

@ -95,9 +95,7 @@ public slots:
// Methods that recurse tree
void forceRedrawEntireTree();
void clearAllNodesBufferIndex();
void cullSharedFaces();
void showCulledSharedFaces();
void setDisableFastVoxelPipeline(bool disableFastVoxelPipeline);
void setUseVoxelShader(bool useVoxelShader);
void setVoxelsAsPoints(bool voxelsAsPoints);

View file

@ -21,9 +21,19 @@
#include "AudioInjector.h"
AudioInjector::AudioInjector(QObject* parent) :
QObject(parent),
_sound(NULL),
_options(),
_shouldStop(false)
{
}
AudioInjector::AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions) :
_sound(sound),
_options(injectorOptions)
_options(injectorOptions),
_shouldStop(false)
{
}
@ -80,7 +90,7 @@ void AudioInjector::injectAudio() {
int numPreAudioDataBytes = injectAudioPacket.size();
// loop to send off our audio in NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL byte chunks
while (currentSendPosition < soundByteArray.size()) {
while (currentSendPosition < soundByteArray.size() && !_shouldStop) {
int bytesToCopy = std::min(NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL,
soundByteArray.size() - currentSendPosition);

View file

@ -24,14 +24,19 @@
class AudioInjector : public QObject {
Q_OBJECT
public:
AudioInjector(QObject* parent);
AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions);
public slots:
void injectAudio();
void stop() { _shouldStop = true; }
signals:
void finished();
private:
Sound* _sound;
AudioInjectorOptions _options;
public slots:
void injectAudio();
signals:
void finished();
bool _shouldStop;
};
Q_DECLARE_METATYPE(AudioInjector*)
#endif // hifi_AudioInjector_h

View file

@ -11,7 +11,7 @@
#include "AudioScriptingInterface.h"
void AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions* injectorOptions) {
AudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions* injectorOptions) {
AudioInjector* injector = new AudioInjector(sound, *injectorOptions);
@ -28,6 +28,18 @@ void AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions
connect(injectorThread, SIGNAL(finished()), injectorThread, SLOT(deleteLater()));
injectorThread->start();
return injector;
}
void AudioScriptingInterface::stopInjector(AudioInjector* injector) {
if (injector) {
injector->stop();
}
}
bool AudioScriptingInterface::isInjectorPlaying(AudioInjector* injector) {
return (injector != NULL);
}
void AudioScriptingInterface::startDrumSound(float volume, float frequency, float duration, float decay,

View file

@ -20,7 +20,9 @@ const AudioInjectorOptions DEFAULT_INJECTOR_OPTIONS;
class AudioScriptingInterface : public QObject {
Q_OBJECT
public slots:
static void playSound(Sound* sound, const AudioInjectorOptions* injectorOptions = NULL);
static AudioInjector* playSound(Sound* sound, const AudioInjectorOptions* injectorOptions = NULL);
static void stopInjector(AudioInjector* injector);
static bool isInjectorPlaying(AudioInjector* injector);
static void startDrumSound(float volume, float frequency, float duration, float decay,
const AudioInjectorOptions* injectorOptions = NULL);

View file

@ -89,7 +89,7 @@ void PositionalAudioRingBuffer::updateNextOutputTrailingLoudness() {
const int TRAILING_AVERAGE_FRAMES = 100;
const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES;
const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO;
const float LOUDNESS_EPSILON = 0.01f;
const float LOUDNESS_EPSILON = 0.000001f;
if (nextLoudness >= _nextOutputTrailingLoudness) {
_nextOutputTrailingLoudness = nextLoudness;

View file

@ -54,6 +54,15 @@ void Extents::addPoint(const glm::vec3& point) {
maximum = glm::max(maximum, point);
}
bool FBXMesh::hasSpecularTexture() const {
foreach (const FBXMeshPart& part, parts) {
if (!part.specularTexture.filename.isEmpty()) {
return true;
}
}
return false;
}
QStringList FBXGeometry::getJointNames() const {
QStringList names;
foreach (const FBXJoint& joint, joints) {
@ -963,6 +972,18 @@ FBXTexture getTexture(const QString& textureID, const QHash<QString, QByteArray>
return texture;
}
bool checkMaterialsHaveTextures(const QHash<QString, Material>& materials,
const QHash<QString, QByteArray>& textureFilenames, const QMultiHash<QString, QString>& childMap) {
foreach (const QString& materialID, materials.keys()) {
foreach (const QString& childID, childMap.values(materialID)) {
if (textureFilenames.contains(childID)) {
return true;
}
}
}
return false;
}
FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) {
QHash<QString, ExtractedMesh> meshes;
QVector<ExtractedBlendshape> blendshapes;
@ -976,6 +997,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
QHash<QString, Material> materials;
QHash<QString, QString> diffuseTextures;
QHash<QString, QString> bumpTextures;
QHash<QString, QString> specularTextures;
QHash<QString, QString> localRotations;
QHash<QString, QString> xComponents;
QHash<QString, QString> yComponents;
@ -1330,6 +1352,9 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
} else if (type.contains("bump") || type.contains("normal")) {
bumpTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
} else if (type.contains("specular")) {
specularTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
} else if (type == "lcl rotation") {
localRotations.insert(getID(connection.properties, 2), getID(connection.properties, 1));
@ -1502,6 +1527,9 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
geometry.bindExtents.reset();
geometry.meshExtents.reset();
// see if any materials have texture children
bool materialsHaveTextures = checkMaterialsHaveTextures(materials, textureFilenames, childMap);
for (QHash<QString, ExtractedMesh>::iterator it = meshes.begin(); it != meshes.end(); it++) {
ExtractedMesh& extracted = it.value();
@ -1546,6 +1574,12 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
generateTangents = true;
}
FBXTexture specularTexture;
QString specularTextureID = specularTextures.value(childID);
if (!specularTextureID.isNull()) {
specularTexture = getTexture(specularTextureID, textureFilenames, textureContent);
}
for (int j = 0; j < extracted.partMaterialTextures.size(); j++) {
if (extracted.partMaterialTextures.at(j).first == materialIndex) {
FBXMeshPart& part = extracted.mesh.parts[j];
@ -1558,6 +1592,9 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
if (!normalTexture.filename.isNull()) {
part.normalTexture = normalTexture;
}
if (!specularTexture.filename.isNull()) {
part.specularTexture = specularTexture;
}
}
}
materialIndex++;
@ -1565,7 +1602,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
} else if (textureFilenames.contains(childID)) {
FBXTexture texture = getTexture(childID, textureFilenames, textureContent);
for (int j = 0; j < extracted.partMaterialTextures.size(); j++) {
if (extracted.partMaterialTextures.at(j).second == textureIndex) {
int partTexture = extracted.partMaterialTextures.at(j).second;
if (partTexture == textureIndex && !(partTexture == 0 && materialsHaveTextures)) {
extracted.mesh.parts[j].diffuseTexture = texture;
}
}

View file

@ -129,6 +129,7 @@ public:
FBXTexture diffuseTexture;
FBXTexture normalTexture;
FBXTexture specularTexture;
};
/// A single mesh (with optional blendshapes) extracted from an FBX document.
@ -150,6 +151,8 @@ public:
bool isEye;
QVector<FBXBlendshape> blendshapes;
bool hasSpecularTexture() const;
};
/// A single animation frame extracted from an FBX document.

View file

@ -46,7 +46,7 @@ const uint16_t MODEL_PACKET_CONTAINS_MODEL_ROTATION = 2048;
const float MODEL_DEFAULT_RADIUS = 0.1f / TREE_SCALE;
const float MINIMUM_MODEL_ELEMENT_SIZE = (1.0f / 100000.0f) / TREE_SCALE; // smallest size container
const QString MODEL_DEFAULT_MODEL_URL("");
const glm::quat MODEL_DEFAULT_MODEL_ROTATION(0, 0, 0, 0);
const glm::quat MODEL_DEFAULT_MODEL_ROTATION;
/// A collection of properties of a model item used in the scripting API. Translates between the actual properties of a model
/// and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete set of
@ -85,6 +85,9 @@ public:
/// used by ModelScriptingInterface to return ModelItemProperties for unknown models
void setIsUnknownID() { _id = UNKNOWN_MODEL_ID; _idSet = true; }
glm::vec3 getMinimumPoint() const { return _position - glm::vec3(_radius, _radius, _radius); }
glm::vec3 getMaximumPoint() const { return _position + glm::vec3(_radius, _radius, _radius); }
private:
glm::vec3 _position;
@ -156,11 +159,20 @@ public:
/// get position in domain scale units (0.0 - 1.0)
const glm::vec3& getPosition() const { return _position; }
glm::vec3 getMinimumPoint() const { return _position - glm::vec3(_radius, _radius, _radius); }
glm::vec3 getMaximumPoint() const { return _position + glm::vec3(_radius, _radius, _radius); }
const rgbColor& getColor() const { return _color; }
xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; }
/// get radius in domain scale units (0.0 - 1.0)
float getRadius() const { return _radius; }
/// get maximum dimension in domain scale units (0.0 - 1.0)
float getSize() const { return _radius * 2.0f; }
/// get maximum dimension in domain scale units (0.0 - 1.0)
AABox getAABox() const { return AABox(getMinimumPoint(), getSize()); }
// model related properties
bool hasModel() const { return !_modelURL.isEmpty(); }

View file

@ -12,7 +12,7 @@
#include "ModelTree.h"
ModelTree::ModelTree(bool shouldReaverage) : Octree(shouldReaverage) {
_rootNode = createNewElement();
_rootElement = createNewElement();
}
ModelTreeElement* ModelTree::createNewElement(unsigned char * octalCode) {
@ -74,71 +74,98 @@ bool ModelTree::findAndDeleteOperation(OctreeElement* element, void* extraData)
return true;
}
class FindAndUpdateModelArgs {
class FindAndUpdateModelOperator : public RecurseOctreeOperator {
public:
const ModelItem& searchModel;
bool found;
FindAndUpdateModelOperator(const ModelItem& searchModel);
virtual bool PreRecursion(OctreeElement* element);
virtual bool PostRecursion(OctreeElement* element);
bool wasFound() const { return _found; }
private:
const ModelItem& _searchModel;
bool _found;
};
bool ModelTree::findAndUpdateOperation(OctreeElement* element, void* extraData) {
FindAndUpdateModelArgs* args = static_cast<FindAndUpdateModelArgs*>(extraData);
FindAndUpdateModelOperator::FindAndUpdateModelOperator(const ModelItem& searchModel) :
_searchModel(searchModel),
_found(false) {
};
bool FindAndUpdateModelOperator::PreRecursion(OctreeElement* element) {
ModelTreeElement* modelTreeElement = static_cast<ModelTreeElement*>(element);
// Note: updateModel() will only operate on correctly found models
if (modelTreeElement->updateModel(args->searchModel)) {
args->found = true;
if (modelTreeElement->updateModel(_searchModel)) {
_found = true;
return false; // stop searching
}
return true;
return !_found; // if we haven't yet found it, keep looking
}
bool FindAndUpdateModelOperator::PostRecursion(OctreeElement* element) {
if (_found) {
element->markWithChangedTime();
}
return !_found; // if we haven't yet found it, keep looking
}
void ModelTree::storeModel(const ModelItem& model, const SharedNodePointer& senderNode) {
// First, look for the existing model in the tree..
FindAndUpdateModelArgs args = { model, false };
recurseTreeWithOperation(findAndUpdateOperation, &args);
FindAndUpdateModelOperator theOperator(model);
recurseTreeWithOperator(&theOperator);
// if we didn't find it in the tree, then store it...
if (!args.found) {
glm::vec3 position = model.getPosition();
float size = std::max(MINIMUM_MODEL_ELEMENT_SIZE, model.getRadius());
ModelTreeElement* element = (ModelTreeElement*)getOrCreateChildElementAt(position.x, position.y, position.z, size);
if (!theOperator.wasFound()) {
AABox modelBox = model.getAABox();
ModelTreeElement* element = (ModelTreeElement*)getOrCreateChildElementContaining(model.getAABox());
element->storeModel(model);
}
// what else do we need to do here to get reaveraging to work
_isDirty = true;
}
class FindAndUpdateModelWithIDandPropertiesArgs {
class FindAndUpdateModelWithIDandPropertiesOperator : public RecurseOctreeOperator {
public:
const ModelItemID& modelID;
const ModelItemProperties& properties;
bool found;
FindAndUpdateModelWithIDandPropertiesOperator(const ModelItemID& modelID, const ModelItemProperties& properties);
virtual bool PreRecursion(OctreeElement* element);
virtual bool PostRecursion(OctreeElement* element);
bool wasFound() const { return _found; }
private:
const ModelItemID& _modelID;
const ModelItemProperties& _properties;
bool _found;
};
bool ModelTree::findAndUpdateWithIDandPropertiesOperation(OctreeElement* element, void* extraData) {
FindAndUpdateModelWithIDandPropertiesArgs* args = static_cast<FindAndUpdateModelWithIDandPropertiesArgs*>(extraData);
FindAndUpdateModelWithIDandPropertiesOperator::FindAndUpdateModelWithIDandPropertiesOperator(const ModelItemID& modelID,
const ModelItemProperties& properties) :
_modelID(modelID),
_properties(properties),
_found(false) {
};
bool FindAndUpdateModelWithIDandPropertiesOperator::PreRecursion(OctreeElement* element) {
ModelTreeElement* modelTreeElement = static_cast<ModelTreeElement*>(element);
// Note: updateModel() will only operate on correctly found models
if (modelTreeElement->updateModel(args->modelID, args->properties)) {
args->found = true;
if (modelTreeElement->updateModel(_modelID, _properties)) {
_found = true;
return false; // stop searching
}
return !_found; // if we haven't yet found it, keep looking
}
// if we've found our model stop searching
if (args->found) {
return false;
bool FindAndUpdateModelWithIDandPropertiesOperator::PostRecursion(OctreeElement* element) {
if (_found) {
element->markWithChangedTime();
}
return true;
return !_found; // if we haven't yet found it, keep looking
}
void ModelTree::updateModel(const ModelItemID& modelID, const ModelItemProperties& properties) {
// First, look for the existing model in the tree..
FindAndUpdateModelWithIDandPropertiesArgs args = { modelID, properties, false };
recurseTreeWithOperation(findAndUpdateWithIDandPropertiesOperation, &args);
// if we found it in the tree, then mark the tree as dirty
if (args.found) {
// Look for the existing model in the tree..
FindAndUpdateModelWithIDandPropertiesOperator theOperator(modelID, properties);
recurseTreeWithOperator(&theOperator);
if (theOperator.wasFound()) {
_isDirty = true;
}
}
@ -467,21 +494,21 @@ void ModelTree::update() {
ModelTreeUpdateArgs args = { };
recurseTreeWithOperation(updateOperation, &args);
// now add back any of the models that moved elements....
// now add back any of the particles that moved elements....
int movingModels = args._movingModels.size();
for (int i = 0; i < movingModels; i++) {
bool shouldDie = args._movingModels[i].getShouldDie();
// if the model is still inside our total bounds, then re-add it
// if the particle is still inside our total bounds, then re-add it
AABox treeBounds = getRoot()->getAABox();
if (!shouldDie && treeBounds.contains(args._movingModels[i].getPosition())) {
storeModel(args._movingModels[i]);
} else {
uint32_t modelID = args._movingModels[i].getID();
uint32_t modelItemID = args._movingModels[i].getID();
quint64 deletedAt = usecTimestampNow();
_recentlyDeletedModelsLock.lockForWrite();
_recentlyDeletedModelItemIDs.insert(deletedAt, modelID);
_recentlyDeletedModelItemIDs.insert(deletedAt, modelItemID);
_recentlyDeletedModelsLock.unlock();
}
}

View file

@ -29,7 +29,7 @@ public:
virtual ModelTreeElement* createNewElement(unsigned char * octalCode = NULL);
/// Type safe version of getRoot()
ModelTreeElement* getRoot() { return (ModelTreeElement*)_rootNode; }
ModelTreeElement* getRoot() { return static_cast<ModelTreeElement*>(_rootElement); }
// These methods will allow the OctreeServer to send your tree inbound edit packets of your

View file

@ -47,15 +47,32 @@ ModelTreeElement* ModelTreeElement::addChildAtIndex(int index) {
}
bool ModelTreeElement::appendElementData(OctreePacketData* packetData) const {
bool ModelTreeElement::appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const {
bool success = true; // assume the best...
// write our models out...
uint16_t numberOfModels = _modelItems->size();
// write our models out... first determine which of the models are in view based on our params
uint16_t numberOfModels = 0;
QVector<uint16_t> indexesOfModelsToInclude;
for (uint16_t i = 0; i < _modelItems->size(); i++) {
if (params.viewFrustum) {
const ModelItem& model = (*_modelItems)[i];
AABox modelBox = model.getAABox();
modelBox.scale(TREE_SCALE);
if (params.viewFrustum->boxInFrustum(modelBox) != ViewFrustum::OUTSIDE) {
indexesOfModelsToInclude << i;
numberOfModels++;
}
} else {
indexesOfModelsToInclude << i;
numberOfModels++;
}
}
success = packetData->appendValue(numberOfModels);
if (success) {
for (uint16_t i = 0; i < numberOfModels; i++) {
foreach (uint16_t i, indexesOfModelsToInclude) {
const ModelItem& model = (*_modelItems)[i];
success = model.appendModelData(packetData);
if (!success) {
@ -66,10 +83,25 @@ bool ModelTreeElement::appendElementData(OctreePacketData* packetData) const {
return success;
}
void ModelTreeElement::update(ModelTreeUpdateArgs& args) {
markWithChangedTime();
// TODO: early exit when _modelItems is empty
bool ModelTreeElement::containsModelBounds(const ModelItem& model) const {
return _box.contains(model.getMinimumPoint()) && _box.contains(model.getMaximumPoint());
}
bool ModelTreeElement::bestFitModelBounds(const ModelItem& model) const {
if (_box.contains(model.getMinimumPoint()) && _box.contains(model.getMaximumPoint())) {
int childForMinimumPoint = getMyChildContainingPoint(model.getMinimumPoint());
int childForMaximumPoint = getMyChildContainingPoint(model.getMaximumPoint());
// If I contain both the minimum and maximum point, but two different children of mine
// contain those points, then I am the best fit for that model
if (childForMinimumPoint != childForMaximumPoint) {
return true;
}
}
return false;
}
void ModelTreeElement::update(ModelTreeUpdateArgs& args) {
// update our contained models
QList<ModelItem>::iterator modelItr = _modelItems->begin();
while(modelItr != _modelItems->end()) {
@ -78,19 +110,18 @@ void ModelTreeElement::update(ModelTreeUpdateArgs& args) {
// If the model wants to die, or if it's left our bounding box, then move it
// into the arguments moving models. These will be added back or deleted completely
if (model.getShouldDie() || !_box.contains(model.getPosition())) {
if (model.getShouldDie() || !bestFitModelBounds(model)) {
args._movingModels.push_back(model);
// erase this model
modelItr = _modelItems->erase(modelItr);
// this element has changed so mark it...
markWithChangedTime();
} else {
++modelItr;
}
}
// TODO: if _modelItems is empty after while loop consider freeing memory in _modelItems if
// internal array is too big (QList internal array does not decrease size except in dtor and
// assignment operator). Otherwise _modelItems could become a "resource leak" for large
// roaming piles of models.
}
bool ModelTreeElement::findSpherePenetration(const glm::vec3& center, float radius,
@ -136,7 +167,9 @@ bool ModelTreeElement::updateModel(const ModelItem& model) {
(localOlder ? "OLDER" : "NEWER"),
difference, debug::valueOf(model.isNewlyCreated()) );
}
thisModel.copyChangedProperties(model);
markWithChangedTime();
} else {
if (wantDebug) {
qDebug(">>> IGNORING SERVER!!! Would've caused jutter! <<< "
@ -167,7 +200,7 @@ bool ModelTreeElement::updateModel(const ModelItemID& modelID, const ModelItemPr
}
if (found) {
thisModel.setProperties(properties);
markWithChangedTime(); // mark our element as changed..
const bool wantDebug = false;
if (wantDebug) {
uint64_t now = usecTimestampNow();

View file

@ -74,7 +74,7 @@ public:
virtual bool requiresSplit() const { return false; }
/// Override to serialize the state of this element. This is used for persistance and for transmission across the network.
virtual bool appendElementData(OctreePacketData* packetData) const;
virtual bool appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const;
/// Override to deserialize the state of this element. This is used for loading from a persisted file or from reading
/// from the network.
@ -118,6 +118,9 @@ public:
bool removeModelWithID(uint32_t id);
bool containsModelBounds(const ModelItem& model) const;
bool bestFitModelBounds(const ModelItem& model) const;
protected:
virtual void init(unsigned char * octalCode);

View file

@ -39,7 +39,7 @@ enum PacketType {
PacketTypeRequestAssignment,
PacketTypeCreateAssignment,
PacketTypeDomainOAuthRequest,
PacketTypeDataServerGet, // reusable
PacketTypeMuteEnvironment,
PacketTypeDataServerSend, // reusable
PacketTypeDataServerConfirm,
PacketTypeVoxelQuery,

File diff suppressed because it is too large Load diff

View file

@ -36,8 +36,15 @@ class Shape;
#include <QObject>
#include <QReadWriteLock>
/// derive from this class to use the Octree::recurseTreeWithOperator() method
class RecurseOctreeOperator {
public:
virtual bool PreRecursion(OctreeElement* element) = 0;
virtual bool PostRecursion(OctreeElement* element) = 0;
};
// Callback function, for recuseTreeWithOperation
typedef bool (*RecurseOctreeOperation)(OctreeElement* node, void* extraData);
typedef bool (*RecurseOctreeOperation)(OctreeElement* element, void* extraData);
typedef enum {GRADIENT, RANDOM, NATURAL} creationMode;
const bool NO_EXISTS_BITS = false;
@ -159,7 +166,7 @@ class ReadBitstreamToTreeParams {
public:
bool includeColor;
bool includeExistsBits;
OctreeElement* destinationNode;
OctreeElement* destinationElement;
QUuid sourceUUID;
SharedNodePointer sourceNode;
bool wantImportProgress;
@ -167,13 +174,13 @@ public:
ReadBitstreamToTreeParams(
bool includeColor = WANT_COLOR,
bool includeExistsBits = WANT_EXISTS_BITS,
OctreeElement* destinationNode = NULL,
OctreeElement* destinationElement = NULL,
QUuid sourceUUID = QUuid(),
SharedNodePointer sourceNode = SharedNodePointer(),
bool wantImportProgress = false) :
includeColor(includeColor),
includeExistsBits(includeExistsBits),
destinationNode(destinationNode),
destinationElement(destinationElement),
sourceUUID(sourceUUID),
sourceNode(sourceNode),
wantImportProgress(wantImportProgress)
@ -200,14 +207,14 @@ public:
virtual void update() { }; // nothing to do by default
OctreeElement* getRoot() { return _rootNode; }
OctreeElement* getRoot() { return _rootElement; }
void eraseAllOctreeElements();
void processRemoveOctreeElementsBitstream(const unsigned char* bitstream, int bufferSizeBytes);
void readBitstreamToTree(const unsigned char* bitstream, unsigned long int bufferSizeBytes, ReadBitstreamToTreeParams& args);
void deleteOctalCodeFromTree(const unsigned char* codeBuffer, bool collapseEmptyTrees = DONT_COLLAPSE);
void reaverageOctreeElements(OctreeElement* startNode = NULL);
void reaverageOctreeElements(OctreeElement* startElement = NULL);
void deleteOctreeElementAt(float x, float y, float z, float s);
@ -220,15 +227,17 @@ public:
OctreeElement* getOctreeEnclosingElementAt(float x, float y, float z, float s) const;
OctreeElement* getOrCreateChildElementAt(float x, float y, float z, float s);
OctreeElement* getOrCreateChildElementContaining(const AABox& box);
void recurseTreeWithOperation(RecurseOctreeOperation operation, void* extraData = NULL);
void recurseTreeWithPostOperation(RecurseOctreeOperation operation, void* extraData = NULL);
void recurseTreeWithOperationDistanceSorted(RecurseOctreeOperation operation,
const glm::vec3& point, void* extraData = NULL);
int encodeTreeBitstream(OctreeElement* node, OctreePacketData* packetData, OctreeElementBag& bag,
void recurseTreeWithOperator(RecurseOctreeOperator* operatorObject);
int encodeTreeBitstream(OctreeElement* element, OctreePacketData* packetData, OctreeElementBag& bag,
EncodeBitstreamParams& params) ;
bool isDirty() const { return _isDirty; }
@ -268,28 +277,30 @@ public:
void loadOctreeFile(const char* fileName, bool wantColorRandomizer);
// these will read/write files that match the wireformat, excluding the 'V' leading
void writeToSVOFile(const char* filename, OctreeElement* node = NULL);
void writeToSVOFile(const char* filename, OctreeElement* element = NULL);
bool readFromSVOFile(const char* filename);
unsigned long getOctreeElementsCount();
void copySubTreeIntoNewTree(OctreeElement* startNode, Octree* destinationTree, bool rebaseToRoot);
void copyFromTreeIntoSubTree(Octree* sourceTree, OctreeElement* destinationNode);
void copySubTreeIntoNewTree(OctreeElement* startElement, Octree* destinationTree, bool rebaseToRoot);
void copyFromTreeIntoSubTree(Octree* sourceTree, OctreeElement* destinationElement);
bool getShouldReaverage() const { return _shouldReaverage; }
void recurseNodeWithOperation(OctreeElement* node, RecurseOctreeOperation operation,
void recurseElementWithOperation(OctreeElement* element, RecurseOctreeOperation operation,
void* extraData, int recursionCount = 0);
/// Traverse child nodes of node applying operation in post-fix order
///
void recurseNodeWithPostOperation(OctreeElement* node, RecurseOctreeOperation operation,
void recurseElementWithPostOperation(OctreeElement* element, RecurseOctreeOperation operation,
void* extraData, int recursionCount = 0);
void recurseNodeWithOperationDistanceSorted(OctreeElement* node, RecurseOctreeOperation operation,
void recurseElementWithOperationDistanceSorted(OctreeElement* element, RecurseOctreeOperation operation,
const glm::vec3& point, void* extraData, int recursionCount = 0);
bool recurseElementWithOperator(OctreeElement* element, RecurseOctreeOperator* operatorObject, int recursionCount = 0);
bool getIsViewing() const { return _isViewing; }
void setIsViewing(bool isViewing) { _isViewing = isViewing; }
@ -302,21 +313,21 @@ public slots:
protected:
void deleteOctalCodeFromTreeRecursion(OctreeElement* node, void* extraData);
void deleteOctalCodeFromTreeRecursion(OctreeElement* element, void* extraData);
int encodeTreeBitstreamRecursion(OctreeElement* node,
int encodeTreeBitstreamRecursion(OctreeElement* element,
OctreePacketData* packetData, OctreeElementBag& bag,
EncodeBitstreamParams& params, int& currentEncodeLevel,
const ViewFrustum::location& parentLocationThisView) const;
static bool countOctreeElementsOperation(OctreeElement* node, void* extraData);
static bool countOctreeElementsOperation(OctreeElement* element, void* extraData);
OctreeElement* nodeForOctalCode(OctreeElement* ancestorNode, const unsigned char* needleCode, OctreeElement** parentOfFoundNode) const;
OctreeElement* createMissingNode(OctreeElement* lastParentNode, const unsigned char* codeToReach);
int readNodeData(OctreeElement *destinationNode, const unsigned char* nodeData,
OctreeElement* nodeForOctalCode(OctreeElement* ancestorElement, const unsigned char* needleCode, OctreeElement** parentOfFoundElement) const;
OctreeElement* createMissingElement(OctreeElement* lastParentElement, const unsigned char* codeToReach);
int readElementData(OctreeElement *destinationElement, const unsigned char* nodeData,
int bufferSizeBytes, ReadBitstreamToTreeParams& args);
OctreeElement* _rootNode;
OctreeElement* _rootElement;
bool _isDirty;
bool _shouldReaverage;

View file

@ -1379,3 +1379,90 @@ OctreeElement* OctreeElement::getOrCreateChildElementAt(float x, float y, float
// Now that we have the child to recurse down, let it answer the original question...
return child->getOrCreateChildElementAt(x, y, z, s);
}
OctreeElement* OctreeElement::getOrCreateChildElementContaining(const AABox& box) {
OctreeElement* child = NULL;
float ourScale = getScale();
float boxScale = box.getScale();
if(boxScale > ourScale) {
qDebug("UNEXPECTED -- OctreeElement::getOrCreateChildElementContaining() "
"boxScale=[%f] > ourScale=[%f] ", boxScale, ourScale);
}
// Determine which of our children the minimum and maximum corners of the box live in...
glm::vec3 boxCornerMinimum = box.getCorner();
glm::vec3 boxCornerMaximum = box.calcTopFarLeft();
int childIndexBoxMinimum = getMyChildContainingPoint(boxCornerMinimum);
int childIndexBoxMaximum = getMyChildContainingPoint(boxCornerMaximum);
// If the minimum and maximum corners of the box are in two different children's boxes, then we are the containing element
if (childIndexBoxMinimum != childIndexBoxMaximum) {
return this;
}
// otherwise, they are the same and that child should be considered as the correct element
int childIndex = childIndexBoxMinimum; // both the same...
// Now, check if we have a child at that location
child = getChildAtIndex(childIndex);
if (!child) {
child = addChildAtIndex(childIndex);
}
// Now that we have the child to recurse down, let it answer the original question...
return child->getOrCreateChildElementContaining(box);
}
int OctreeElement::getMyChildContainingPoint(const glm::vec3& point) const {
glm::vec3 ourCenter = _box.calcCenter();
int childIndex = CHILD_UNKNOWN;
// left half
if (point.x > ourCenter.x) {
if (point.y > ourCenter.y) {
// top left
if (point.z > ourCenter.z) {
// top left far
childIndex = CHILD_TOP_LEFT_FAR;
} else {
// top left near
childIndex = CHILD_TOP_LEFT_NEAR;
}
} else {
// bottom left
if (point.z > ourCenter.z) {
// bottom left far
childIndex = CHILD_BOTTOM_LEFT_FAR;
} else {
// bottom left near
childIndex = CHILD_BOTTOM_LEFT_NEAR;
}
}
} else {
// right half
if (point.y > ourCenter.y) {
// top right
if (point.z > ourCenter.z) {
// top right far
childIndex = CHILD_TOP_RIGHT_FAR;
} else {
// top right near
childIndex = CHILD_TOP_RIGHT_NEAR;
}
} else {
// bottom right
if (point.z > ourCenter.z) {
// bottom right far
childIndex = CHILD_BOTTOM_RIGHT_FAR;
} else {
// bottom right near
childIndex = CHILD_BOTTOM_RIGHT_NEAR;
}
}
}
return childIndex;
}

View file

@ -23,14 +23,14 @@
#include "AABox.h"
#include "ViewFrustum.h"
#include "OctreeConstants.h"
//#include "Octree.h"
class EncodeBitstreamParams;
class Octree;
class OctreeElement;
class OctreeElementDeleteHook;
class OctreePacketData;
class VoxelSystem;
class ReadBitstreamToTreeParams;
class VoxelSystem;
// Callers who want delete hook callbacks should implement this class
class OctreeElementDeleteHook {
@ -81,7 +81,7 @@ public:
virtual bool requiresSplit() const { return false; }
/// Override to serialize the state of this element. This is used for persistance and for transmission across the network.
virtual bool appendElementData(OctreePacketData* packetData) const { return true; }
virtual bool appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const { return true; }
/// Override to deserialize the state of this element. This is used for loading from a persisted file or from reading
/// from the network.
@ -217,6 +217,8 @@ public:
OctreeElement* getOrCreateChildElementAt(float x, float y, float z, float s);
OctreeElement* getOrCreateChildElementContaining(const AABox& box);
int getMyChildContainingPoint(const glm::vec3& point) const;
protected:

View file

@ -33,7 +33,7 @@ public:
virtual void renderElement(OctreeElement* element, RenderArgs* args) { /* swallow these */ };
virtual void init();
virtual void render() { /* swallow these */ };
virtual void render(RenderMode renderMode = DEFAULT_RENDER_MODE) { /* swallow these */ };
void setJurisdictionListener(JurisdictionListener* jurisdictionListener) { _jurisdictionListener = jurisdictionListener; }

View file

@ -28,6 +28,7 @@
typedef unsigned char OCTREE_PACKET_FLAGS;
typedef uint16_t OCTREE_PACKET_SEQUENCE;
const uint16_t MAX_OCTREE_PACKET_SEQUENCE = 65535;
typedef quint64 OCTREE_PACKET_SENT_TIME;
typedef uint16_t OCTREE_PACKET_INTERNAL_SECTION_SIZE;
const int MAX_OCTREE_PACKET_SIZE = MAX_PACKET_SIZE;

View file

@ -154,8 +154,8 @@ bool OctreeRenderer::renderOperation(OctreeElement* element, void* extraData) {
return false;
}
void OctreeRenderer::render() {
RenderArgs args = { 0, this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust() };
void OctreeRenderer::render(RenderMode renderMode) {
RenderArgs args = { 0, this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust(), renderMode };
if (_tree) {
_tree->lockForRead();
_tree->recurseTreeWithOperation(renderOperation, &args);

View file

@ -25,15 +25,7 @@
#include "ViewFrustum.h"
class OctreeRenderer;
class RenderArgs {
public:
int _renderedItems;
OctreeRenderer* _renderer;
ViewFrustum* _viewFrustum;
float _sizeScale;
int _boundaryLevelAdjust;
};
class RenderArgs;
// Generic client side Octree renderer class.
@ -59,8 +51,10 @@ public:
/// initialize and GPU/rendering related resources
virtual void init();
enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE };
/// render the content of the octree
virtual void render();
virtual void render(RenderMode renderMode = DEFAULT_RENDER_MODE);
ViewFrustum* getViewFrustum() const { return _viewFrustum; }
void setViewFrustum(ViewFrustum* viewFrustum) { _viewFrustum = viewFrustum; }
@ -75,4 +69,15 @@ protected:
ViewFrustum* _viewFrustum;
};
class RenderArgs {
public:
int _renderedItems;
OctreeRenderer* _renderer;
ViewFrustum* _viewFrustum;
float _sizeScale;
int _boundaryLevelAdjust;
OctreeRenderer::RenderMode _renderMode;
};
#endif // hifi_OctreeRenderer_h

View file

@ -875,10 +875,13 @@ void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
return; // ignore any packets that are unreasonable
}
// determine our expected sequence number... handle rollover appropriately
OCTREE_PACKET_SEQUENCE expected = _incomingPacket > 0 ? _incomingLastSequence + 1 : sequence;
// Guard against possible corrupted packets... with bad sequence numbers
const int MAX_RESONABLE_SEQUENCE_OFFSET = 2000;
const int MIN_RESONABLE_SEQUENCE_OFFSET = -2000;
int sequenceOffset = (sequence - _incomingLastSequence);
int sequenceOffset = (sequence - expected);
if (sequenceOffset > MAX_RESONABLE_SEQUENCE_OFFSET || sequenceOffset < MIN_RESONABLE_SEQUENCE_OFFSET) {
qDebug() << "ignoring unreasonable packet... sequence:" << sequence << "_incomingLastSequence:" << _incomingLastSequence;
return; // ignore any packets that are unreasonable
@ -901,7 +904,6 @@ void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
qDebug() << "last packet duplicate got:" << sequence << "_incomingLastSequence:" << _incomingLastSequence;
}
} else {
OCTREE_PACKET_SEQUENCE expected = _incomingLastSequence+1;
if (sequence != expected) {
if (wantExtraDebugging) {
qDebug() << "out of order... got:" << sequence << "expected:" << expected;
@ -958,9 +960,9 @@ void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
}
}
// only bump the last sequence if it was greater than our previous last sequence, this will keep us from
// only bump the last sequence if it was greater than our expected sequence, this will keep us from
// accidentally going backwards when an out of order (recovered) packet comes in
if (sequence > _incomingLastSequence) {
if (sequence >= expected) {
_incomingLastSequence = sequence;
}

View file

@ -12,7 +12,7 @@
#include "ParticleTree.h"
ParticleTree::ParticleTree(bool shouldReaverage) : Octree(shouldReaverage) {
_rootNode = createNewElement();
_rootElement = createNewElement();
}
ParticleTreeElement* ParticleTree::createNewElement(unsigned char * octalCode) {

View file

@ -29,7 +29,7 @@ public:
virtual ParticleTreeElement* createNewElement(unsigned char * octalCode = NULL);
/// Type safe version of getRoot()
ParticleTreeElement* getRoot() { return (ParticleTreeElement*)_rootNode; }
ParticleTreeElement* getRoot() { return static_cast<ParticleTreeElement*>(_rootElement); }
// These methods will allow the OctreeServer to send your tree inbound edit packets of your

View file

@ -47,7 +47,7 @@ ParticleTreeElement* ParticleTreeElement::addChildAtIndex(int index) {
}
bool ParticleTreeElement::appendElementData(OctreePacketData* packetData) const {
bool ParticleTreeElement::appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const {
bool success = true; // assume the best...
// write our particles out...

View file

@ -76,7 +76,7 @@ public:
virtual bool requiresSplit() const { return false; }
/// Override to serialize the state of this element. This is used for persistance and for transmission across the network.
virtual bool appendElementData(OctreePacketData* packetData) const;
virtual bool appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const;
/// Override to deserialize the state of this element. This is used for loading from a persisted file or from reading
/// from the network.

View file

@ -18,6 +18,7 @@
#include <QtNetwork/QNetworkReply>
#include <QScriptEngine>
#include <AudioInjector.h>
#include <AudioRingBuffer.h>
#include <AvatarData.h>
#include <CollisionInfo.h>
@ -52,6 +53,14 @@ static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){
return QScriptValue();
}
QScriptValue injectorToScriptValue(QScriptEngine *engine, AudioInjector* const &in) {
return engine->newQObject(in);
}
void injectorFromScriptValue(const QScriptValue &object, AudioInjector* &out) {
out = qobject_cast<AudioInjector*>(object.toQObject());
}
ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString,
AbstractControllerScriptingInterface* controllerScriptingInterface) :
@ -226,6 +235,8 @@ void ScriptEngine::init() {
QScriptValue localVoxelsValue = _engine.scriptValueFromQMetaObject<LocalVoxels>();
_engine.globalObject().setProperty("LocalVoxels", localVoxelsValue);
qScriptRegisterMetaType(&_engine, injectorToScriptValue, injectorFromScriptValue);
registerGlobalObject("Script", this);
registerGlobalObject("Audio", &_audioScriptingInterface);

View file

@ -23,13 +23,13 @@
VoxelTree::VoxelTree(bool shouldReaverage) : Octree(shouldReaverage)
{
_rootNode = createNewElement();
_rootElement = createNewElement();
}
VoxelTreeElement* VoxelTree::createNewElement(unsigned char * octalCode) {
VoxelSystem* voxelSystem = NULL;
if (_rootNode) {
voxelSystem = ((VoxelTreeElement*)_rootNode)->getVoxelSystem();
if (_rootElement) {
voxelSystem = (static_cast<VoxelTreeElement*>(_rootElement))->getVoxelSystem();
}
VoxelTreeElement* newElement = new VoxelTreeElement(octalCode);
newElement->setVoxelSystem(voxelSystem);

View file

@ -26,7 +26,7 @@ public:
VoxelTree(bool shouldReaverage = false);
virtual VoxelTreeElement* createNewElement(unsigned char * octalCode = NULL);
VoxelTreeElement* getRoot() { return (VoxelTreeElement*)_rootNode; }
VoxelTreeElement* getRoot() { return static_cast<VoxelTreeElement*>(_rootElement); }
void deleteVoxelAt(float x, float y, float z, float s);

View file

@ -65,7 +65,7 @@ void VoxelTreeElement::splitChildren() {
}
}
bool VoxelTreeElement::appendElementData(OctreePacketData* packetData) const {
bool VoxelTreeElement::appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const {
return packetData->appendColor(getColor());
}

View file

@ -43,7 +43,7 @@ public:
virtual bool hasContent() const { return isColored(); }
virtual void splitChildren();
virtual bool requiresSplit() const;
virtual bool appendElementData(OctreePacketData* packetData) const;
virtual bool appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const;
virtual int readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args);
virtual void calculateAverageFromChildren();
virtual bool collapseChildren();