This commit is contained in:
barnold1953 2014-05-30 09:26:07 -07:00
commit 06a9ac22e7
32 changed files with 1247 additions and 267 deletions

View file

@ -40,6 +40,8 @@ var modelURLs = [
var toolBar;
var jointList = MyAvatar.getJointNames();
function isLocked(properties) {
// special case to lock the ground plane model in hq.
if (location.hostname == "hq.highfidelity.io" &&
@ -81,10 +83,13 @@ function controller(wichSide) {
this.grabbing = false;
this.modelID = { isKnownID: false };
this.modelURL = "";
this.oldModelRotation;
this.oldModelPosition;
this.oldModelRadius;
this.jointsIntersectingFromStart = [];
this.laser = Overlays.addOverlay("line3d", {
position: { x: 0, y: 0, z: 0 },
end: { x: 0, y: 0, z: 0 },
@ -134,16 +139,65 @@ function controller(wichSide) {
this.grabbing = true;
this.modelID = modelID;
this.modelURL = properties.modelURL;
this.oldModelPosition = properties.position;
this.oldModelRotation = properties.modelRotation;
this.oldModelRadius = properties.radius;
this.jointsIntersectingFromStart = [];
for (var i = 0; i < jointList.length; i++) {
var distance = Vec3.distance(MyAvatar.getJointPosition(jointList[i]), this.oldModelPosition);
if (distance < this.oldModelRadius) {
this.jointsIntersectingFromStart.push(i);
}
}
}
}
this.release = function () {
if (this.grabbing) {
if (jointList.length <= 0) {
jointList = MyAvatar.getJointNames();
}
var closestJointIndex = -1;
var closestJointDistance = 10;
for (var i = 0; i < jointList.length; i++) {
var distance = Vec3.distance(MyAvatar.getJointPosition(jointList[i]), this.oldModelPosition);
if (distance < closestJointDistance) {
closestJointDistance = distance;
closestJointIndex = i;
}
}
print("closestJoint: " + jointList[closestJointIndex]);
print("closestJointDistance (attach max distance): " + closestJointDistance + " (" + this.oldModelRadius + ")");
if (closestJointDistance < this.oldModelRadius) {
if (this.jointsIntersectingFromStart.indexOf(closestJointIndex) != -1) {
// Do nothing
} else {
print("Attaching to " + jointList[closestJointIndex]);
var jointPosition = MyAvatar.getJointPosition(jointList[closestJointIndex]);
var jointRotation = MyAvatar.getJointCombinedRotation(jointList[closestJointIndex]);
var attachmentOffset = Vec3.subtract(this.oldModelPosition, jointPosition);
attachmentOffset = Vec3.multiplyQbyV(Quat.inverse(jointRotation), attachmentOffset);
var attachmentRotation = Quat.multiply(Quat.inverse(jointRotation), this.oldModelRotation);
MyAvatar.attach(this.modelURL, jointList[closestJointIndex],
attachmentOffset, attachmentRotation, 2.0 * this.oldModelRadius,
true, false);
Models.deleteModel(this.modelID);
}
}
}
this.grabbing = false;
this.modelID.isKnownID = false;
this.jointsIntersectingFromStart = [];
}
this.checkTrigger = function () {
@ -251,11 +305,6 @@ function controller(wichSide) {
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;
@ -410,8 +459,6 @@ function checkController(deltaTime) {
moveOverlays();
}
function initToolBar() {
toolBar = new ToolBar(0, 0, ToolBar.VERTICAL);
// New Model

View file

@ -28,8 +28,10 @@ var NEW_VOXEL_SIZE = 1.0;
var NEW_VOXEL_DISTANCE_FROM_CAMERA = 3.0;
var PIXELS_PER_EXTRUDE_VOXEL = 16;
var WHEEL_PIXELS_PER_SCALE_CHANGE = 100;
var MAX_VOXEL_SCALE = 16.0;
var MIN_VOXEL_SCALE = 1.0 / Math.pow(2.0, 8.0);
var MAX_VOXEL_SCALE_POWER = 4;
var MIN_VOXEL_SCALE_POWER = -8;
var MAX_VOXEL_SCALE = Math.pow(2.0, MAX_VOXEL_SCALE_POWER);
var MIN_VOXEL_SCALE = Math.pow(2.0, MIN_VOXEL_SCALE_POWER);
var WHITE_COLOR = { red: 255, green: 255, blue: 255 };
var MAX_PASTE_VOXEL_SCALE = 256;
@ -330,6 +332,13 @@ function ScaleSelector() {
visible: false
});
this.setScale = function(scale) {
if (scale > MAX_VOXEL_SCALE) {
scale = MAX_VOXEL_SCALE;
}
if (scale < MIN_VOXEL_SCALE) {
scale = MIN_VOXEL_SCALE;
}
this.scale = scale;
this.power = Math.floor(Math.log(scale) / Math.log(2));
rescaleImport();
@ -391,12 +400,9 @@ function ScaleSelector() {
this.incrementScale = function() {
copyScale = false;
if (this.power < 13) {
if (this.power < MAX_VOXEL_SCALE_POWER) {
++this.power;
this.scale *= 2.0;
if (this.scale > MAX_VOXEL_SCALE) {
this.scale = MAX_VOXEL_SCALE;
}
this.update();
rescaleImport();
resizeVoxelSound.play(voxelSizePlus);
@ -405,7 +411,7 @@ function ScaleSelector() {
this.decrementScale = function() {
copyScale = false;
if (-4 < this.power) {
if (MIN_VOXEL_SCALE_POWER < this.power) {
--this.power;
this.scale /= 2.0;
this.update();

43
examples/playSoundLoop.js Normal file
View file

@ -0,0 +1,43 @@
//
// playSoundLoop.js
// examples
//
// Created by David Rowe on 5/29/14.
// Copyright 2014 High Fidelity, Inc.
//
// This example script plays a sound in a continuous loop.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var sound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Nylon+A.raw");
var soundPlaying = false;
function keyPressEvent(event) {
if (event.text === "1") {
if (!Audio.isInjectorPlaying(soundPlaying)) {
var options = new AudioInjectionOptions();
options.position = MyAvatar.position;
options.volume = 0.5;
options.loop = true;
soundPlaying = Audio.playSound(sound, options);
print("Started sound loop");
} else {
Audio.stopInjector(soundPlaying);
print("Stopped sound loop");
}
}
}
function scriptEnding() {
if (Audio.isInjectorPlaying(soundPlaying)) {
Audio.stopInjector(soundPlaying);
print("Stopped sound loop");
}
}
// Connect a call back that happens every frame
Script.scriptEnding.connect(scriptEnding);
Controller.keyPressEvent.connect(keyPressEvent);

View file

@ -0,0 +1,40 @@
#version 120
//
// cascaded_shadow_map.frag
// fragment shader
//
// Created by Andrzej Kapolka on 5/29/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 shadow texture
uniform sampler2DShadow shadowMap;
// the distances to the cascade sections
uniform vec3 shadowDistances;
// the inverse of the size of the shadow map
const float shadowScale = 1.0 / 2048.0;
// the color in shadow
varying vec4 shadowColor;
// the interpolated position
varying vec4 position;
void main(void) {
// compute the index of the cascade to use and the corresponding texture coordinates
int shadowIndex = int(dot(step(vec3(position.z), shadowDistances), vec3(1.0, 1.0, 1.0)));
vec3 shadowTexCoord = vec3(dot(gl_EyePlaneS[shadowIndex], position), dot(gl_EyePlaneT[shadowIndex], position),
dot(gl_EyePlaneR[shadowIndex], position));
gl_FragColor = mix(shadowColor, gl_Color, 0.25 *
(shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, shadowScale, 0.0)).r));
}

View file

@ -0,0 +1,33 @@
#version 120
//
// cascaded_shadow_map.vert
// vertex shader
//
// Created by Andrzej Kapolka on 5/29/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 color in shadow
varying vec4 shadowColor;
// the interpolated position
varying vec4 position;
void main(void) {
// the shadow color includes only the ambient terms
shadowColor = gl_Color * (gl_LightModel.ambient + gl_LightSource[0].ambient);
// the normal color includes diffuse
vec4 normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0));
gl_FrontColor = shadowColor + gl_Color * (gl_LightSource[0].diffuse * max(0.0, dot(normal, gl_LightSource[0].position)));
// generate the shadow texture coordinates using the eye position
position = gl_ModelViewMatrix * gl_Vertex;
// use the fixed function transform
gl_Position = ftransform();
}

View file

@ -0,0 +1,56 @@
#version 120
//
// model_cascaded_shadow_map.frag
// fragment shader
//
// Created by Andrzej Kapolka on 5/29/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 shadow texture
uniform sampler2DShadow shadowMap;
// the distances to the cascade sections
uniform vec3 shadowDistances;
// the inverse of the size of the shadow map
const float shadowScale = 1.0 / 2048.0;
// the interpolated position
varying vec4 position;
// the interpolated normal
varying vec4 normal;
void main(void) {
// compute the index of the cascade to use and the corresponding texture coordinates
int shadowIndex = int(dot(step(vec3(position.z), shadowDistances), vec3(1.0, 1.0, 1.0)));
vec3 shadowTexCoord = vec3(dot(gl_EyePlaneS[shadowIndex], position), dot(gl_EyePlaneT[shadowIndex], position),
dot(gl_EyePlaneR[shadowIndex], position));
// compute the base color based on OpenGL lighting model
vec4 normalizedNormal = normalize(normal);
float diffuse = dot(normalizedNormal, gl_LightSource[0].position);
float facingLight = step(0.0, diffuse) * 0.25 *
(shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, shadowScale, 0.0)).r);
vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
// compute the specular component (sans exponent)
float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position - normalize(vec4(position.xyz, 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, 0.0);
}

View file

@ -0,0 +1,69 @@
#version 120
//
// model_cascaded_shadow_normal_map.frag
// fragment shader
//
// Created by Andrzej Kapolka on 5/29/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 shadow texture
uniform sampler2DShadow shadowMap;
// the distances to the cascade sections
uniform vec3 shadowDistances;
// the inverse of the size of the shadow map
const float shadowScale = 1.0 / 2048.0;
// the interpolated position
varying vec4 interpolatedPosition;
// 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 index of the cascade to use and the corresponding texture coordinates
int shadowIndex = int(dot(step(vec3(interpolatedPosition.z), shadowDistances), vec3(1.0, 1.0, 1.0)));
vec3 shadowTexCoord = vec3(dot(gl_EyePlaneS[shadowIndex], interpolatedPosition),
dot(gl_EyePlaneT[shadowIndex], interpolatedPosition),
dot(gl_EyePlaneR[shadowIndex], interpolatedPosition));
// compute the base color based on OpenGL lighting model
vec4 viewNormal = vec4(normalizedTangent * localNormal.x +
normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0);
float diffuse = dot(viewNormal, gl_LightSource[0].position);
float facingLight = step(0.0, diffuse) * 0.25 *
(shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, shadowScale, 0.0)).r);
vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
// compute the specular component (sans exponent)
float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position -
normalize(vec4(vec3(interpolatedPosition), 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, 0.0);
}

View file

@ -0,0 +1,72 @@
#version 120
//
// model_cascaded_shadow_normal_specular_map.frag
// fragment shader
//
// Created by Andrzej Kapolka on 5/29/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 shadow texture
uniform sampler2DShadow shadowMap;
// the distances to the cascade sections
uniform vec3 shadowDistances;
// the inverse of the size of the shadow map
const float shadowScale = 1.0 / 2048.0;
// the interpolated position
varying vec4 interpolatedPosition;
// 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 index of the cascade to use and the corresponding texture coordinates
int shadowIndex = int(dot(step(vec3(interpolatedPosition.z), shadowDistances), vec3(1.0, 1.0, 1.0)));
vec3 shadowTexCoord = vec3(dot(gl_EyePlaneS[shadowIndex], interpolatedPosition),
dot(gl_EyePlaneT[shadowIndex], interpolatedPosition),
dot(gl_EyePlaneR[shadowIndex], interpolatedPosition));
// compute the base color based on OpenGL lighting model
vec4 viewNormal = vec4(normalizedTangent * localNormal.x +
normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0);
float diffuse = dot(viewNormal, gl_LightSource[0].position);
float facingLight = step(0.0, diffuse) * 0.25 *
(shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, shadowScale, 0.0)).r);
vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
// compute the specular component (sans exponent)
float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position -
normalize(vec4(interpolatedPosition.xyz, 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,59 @@
#version 120
//
// model_cascaded_shadow_specular_map.frag
// fragment shader
//
// Created by Andrzej Kapolka on 5/29/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 shadow texture
uniform sampler2DShadow shadowMap;
// the distances to the cascade sections
uniform vec3 shadowDistances;
// the inverse of the size of the shadow map
const float shadowScale = 1.0 / 2048.0;
// the interpolated position in view space
varying vec4 position;
// the interpolated normal
varying vec4 normal;
void main(void) {
// compute the index of the cascade to use and the corresponding texture coordinates
int shadowIndex = int(dot(step(vec3(position.z), shadowDistances), vec3(1.0, 1.0, 1.0)));
vec3 shadowTexCoord = vec3(dot(gl_EyePlaneS[shadowIndex], position), dot(gl_EyePlaneT[shadowIndex], position),
dot(gl_EyePlaneR[shadowIndex], position));
// compute the base color based on OpenGL lighting model
vec4 normalizedNormal = normalize(normal);
float diffuse = dot(normalizedNormal, gl_LightSource[0].position);
float facingLight = step(0.0, diffuse) * 0.25 *
(shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, shadowScale, 0.0)).r);
vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
// compute the specular component (sans exponent)
float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position - normalize(vec4(position.xyz, 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

@ -16,6 +16,7 @@ uniform sampler2DShadow shadowMap;
// the inverse of the size of the shadow map
const float shadowScale = 1.0 / 2048.0;
// the color in shadow
varying vec4 shadowColor;
void main(void) {

View file

@ -11,6 +11,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// the color in shadow
varying vec4 shadowColor;
void main(void) {
@ -21,10 +22,10 @@ void main(void) {
vec4 normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0));
gl_FrontColor = shadowColor + gl_Color * (gl_LightSource[0].diffuse * max(0.0, dot(normal, gl_LightSource[0].position)));
// generate the shadow texture coordinate using the eye position
// generate the shadow texture coordinates using the eye position
vec4 eyePosition = gl_ModelViewMatrix * gl_Vertex;
gl_TexCoord[0] = vec4(dot(gl_EyePlaneS[0], eyePosition), dot(gl_EyePlaneT[0], eyePosition),
dot(gl_EyePlaneR[0], eyePosition), 1.0);
dot(gl_EyePlaneR[0], eyePosition), 1.0);
// use the fixed function transform
gl_Position = ftransform();

View file

@ -554,6 +554,8 @@ void Application::initializeGL() {
}
void Application::paintGL() {
PerformanceTimer perfTimer("paintGL");
PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings));
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::paintGL()");
@ -618,7 +620,7 @@ void Application::paintGL() {
whichCamera = _viewFrustumOffsetCamera;
}
if (Menu::getInstance()->isOptionChecked(MenuOption::Shadows)) {
if (Menu::getInstance()->getShadowsEnabled()) {
updateShadowMap();
}
@ -648,7 +650,10 @@ void Application::paintGL() {
_rearMirrorTools->render(true);
}
_applicationOverlay.renderOverlay();
{
PerformanceTimer perfTimer("paintGL/renderOverlay");
_applicationOverlay.renderOverlay();
}
}
_frameCount++;
@ -1288,11 +1293,13 @@ void Application::timer() {
}
void Application::idle() {
PerformanceTimer perfTimer("idle");
// Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing
// details if we're in ExtraDebugging mode. However, the ::update() and it's subcomponents will show their timing
// details normally.
bool showWarnings = getLogger()->extraDebugging();
PerformanceWarning warn(showWarnings, "Application::idle()");
PerformanceWarning warn(showWarnings, "idle()");
// Only run simulation code if more than IDLE_SIMULATE_MSECS have passed since last time we ran
@ -1300,15 +1307,18 @@ void Application::idle() {
if (timeSinceLastUpdate > IDLE_SIMULATE_MSECS) {
_lastTimeUpdated.start();
{
PerformanceTimer perfTimer("idle/update");
PerformanceWarning warn(showWarnings, "Application::idle()... update()");
const float BIGGEST_DELTA_TIME_SECS = 0.25f;
update(glm::clamp((float)timeSinceLastUpdate / 1000.f, 0.f, BIGGEST_DELTA_TIME_SECS));
}
{
PerformanceTimer perfTimer("idle/updateGL");
PerformanceWarning warn(showWarnings, "Application::idle()... updateGL()");
_glWidget->updateGL();
}
{
PerformanceTimer perfTimer("idle/rest");
PerformanceWarning warn(showWarnings, "Application::idle()... rest of it");
_idleLoopStdev.addValue(timeSinceLastUpdate);
@ -1320,14 +1330,16 @@ void Application::idle() {
}
if (Menu::getInstance()->isOptionChecked(MenuOption::BuckyBalls)) {
PerformanceTimer perfTimer("idle/rest/_buckyBalls");
_buckyBalls.simulate(timeSinceLastUpdate / 1000.f, Application::getInstance()->getAvatar()->getHandData());
}
// After finishing all of the above work, restart the idle timer, allowing 2ms to process events.
idleTimer->start(2);
}
if (_numChangedSettings > 0) {
saveSettings();
if (_numChangedSettings > 0) {
saveSettings();
}
}
}
}
@ -1729,6 +1741,7 @@ bool Application::isLookingAtMyAvatar(Avatar* avatar) {
}
void Application::updateLOD() {
PerformanceTimer perfTimer("idle/update/updateLOD");
// adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode
if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableAutoAdjustLOD) && !isThrottleRendering()) {
Menu::getInstance()->autoAdjustLOD(_fps);
@ -1738,6 +1751,7 @@ void Application::updateLOD() {
}
void Application::updateMouseRay() {
PerformanceTimer perfTimer("idle/update/updateMouseRay");
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateMouseRay()");
@ -1770,6 +1784,7 @@ void Application::updateMouseRay() {
}
void Application::updateFaceshift() {
PerformanceTimer perfTimer("idle/update/updateFaceshift");
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateFaceshift()");
@ -1784,6 +1799,7 @@ void Application::updateFaceshift() {
}
void Application::updateVisage() {
PerformanceTimer perfTimer("idle/update/updateVisage");
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateVisage()");
@ -1793,6 +1809,7 @@ void Application::updateVisage() {
}
void Application::updateMyAvatarLookAtPosition() {
PerformanceTimer perfTimer("idle/update/updateMyAvatarLookAtPosition");
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateMyAvatarLookAtPosition()");
@ -1858,6 +1875,7 @@ void Application::updateMyAvatarLookAtPosition() {
}
void Application::updateThreads(float deltaTime) {
PerformanceTimer perfTimer("idle/update/updateThreads");
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateThreads()");
@ -1872,6 +1890,7 @@ void Application::updateThreads(float deltaTime) {
}
void Application::updateMetavoxels(float deltaTime) {
PerformanceTimer perfTimer("idle/update/updateMetavoxels");
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateMetavoxels()");
@ -1901,6 +1920,7 @@ void Application::cameraMenuChanged() {
}
void Application::updateCamera(float deltaTime) {
PerformanceTimer perfTimer("idle/update/updateCamera");
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateCamera()");
@ -1918,6 +1938,7 @@ void Application::updateCamera(float deltaTime) {
}
void Application::updateDialogs(float deltaTime) {
PerformanceTimer perfTimer("idle/update/updateDialogs");
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateDialogs()");
@ -1934,6 +1955,7 @@ void Application::updateDialogs(float deltaTime) {
}
void Application::updateCursor(float deltaTime) {
PerformanceTimer perfTimer("idle/update/updateCursor");
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateCursor()");
@ -1958,51 +1980,87 @@ void Application::updateCursor(float deltaTime) {
}
void Application::update(float deltaTime) {
//PerformanceTimer perfTimer("idle/update"); // NOTE: we track this above in Application::idle()
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::update()");
updateLOD();
// check what's under the mouse and update the mouse voxel
updateMouseRay();
updateMouseRay(); // check what's under the mouse and update the mouse voxel
updateFaceshift();
updateVisage();
_myAvatar->updateLookAtTargetAvatar();
{
PerformanceTimer perfTimer("idle/update/updateLookAtTargetAvatar");
_myAvatar->updateLookAtTargetAvatar();
}
updateMyAvatarLookAtPosition();
_sixenseManager.update(deltaTime);
_joystickManager.update();
_prioVR.update(deltaTime);
updateMyAvatar(deltaTime); // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes
{
PerformanceTimer perfTimer("idle/update/sixense,joystick,prioVR");
_sixenseManager.update(deltaTime);
_joystickManager.update();
_prioVR.update(deltaTime);
}
{
PerformanceTimer perfTimer("idle/update/updateMyAvatar");
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...
{
PerformanceTimer perfTimer("idle/update/_avatarManager");
_avatarManager.updateOtherAvatars(deltaTime); //loop through all the other avatars and simulate them...
}
updateMetavoxels(deltaTime); // update metavoxels
updateCamera(deltaTime); // handle various camera tweaks like off axis projection
updateDialogs(deltaTime); // update various stats dialogs if present
updateCursor(deltaTime); // Handle cursor updates
_particles.update(); // update the particles...
_particleCollisionSystem.update(); // collide the particles...
{
PerformanceTimer perfTimer("idle/update/_particles");
_particles.update(); // update the particles...
}
{
PerformanceTimer perfTimer("idle/update/_particleCollisionSystem");
_particleCollisionSystem.update(); // collide the particles...
}
_models.update(); // update the models...
{
PerformanceTimer perfTimer("idle/update/_models");
_models.update(); // update the models...
}
_overlays.update(deltaTime);
{
PerformanceTimer perfTimer("idle/update/_overlays");
_overlays.update(deltaTime);
}
// let external parties know we're updating
emit simulating(deltaTime);
{
PerformanceTimer perfTimer("idle/update/emit simulating");
// let external parties know we're updating
emit simulating(deltaTime);
}
}
void Application::updateMyAvatar(float deltaTime) {
PerformanceTimer perfTimer("updateMyAvatar");
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateMyAvatar()");
_myAvatar->update(deltaTime);
{
PerformanceTimer perfTimer("updateMyAvatar/_myAvatar->update()");
_myAvatar->update(deltaTime);
}
// send head/hand data to the avatar mixer and voxel server
QByteArray packet = byteArrayWithPopulatedHeader(PacketTypeAvatarData);
packet.append(_myAvatar->toByteArray());
controlledBroadcastToNodes(packet, NodeSet() << NodeType::AvatarMixer);
{
// send head/hand data to the avatar mixer and voxel server
PerformanceTimer perfTimer("updateMyAvatar/sendToAvatarMixer");
QByteArray packet = byteArrayWithPopulatedHeader(PacketTypeAvatarData);
packet.append(_myAvatar->toByteArray());
controlledBroadcastToNodes(packet, NodeSet() << NodeType::AvatarMixer);
}
// Update _viewFrustum with latest camera and view frustum data...
// NOTE: we get this from the view frustum, to make it simpler, since the
@ -2010,22 +2068,28 @@ void Application::updateMyAvatar(float deltaTime) {
// We could optimize this to not actually load the viewFrustum, since we don't
// actually need to calculate the view frustum planes to send these details
// to the server.
loadViewFrustum(_myCamera, _viewFrustum);
{
PerformanceTimer perfTimer("updateMyAvatar/loadViewFrustum");
loadViewFrustum(_myCamera, _viewFrustum);
}
// Update my voxel servers with my current voxel query...
quint64 now = usecTimestampNow();
quint64 sinceLastQuery = now - _lastQueriedTime;
const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND;
bool queryIsDue = sinceLastQuery > TOO_LONG_SINCE_LAST_QUERY;
bool viewIsDifferentEnough = !_lastQueriedViewFrustum.isVerySimilar(_viewFrustum);
{
PerformanceTimer perfTimer("updateMyAvatar/queryOctree");
quint64 now = usecTimestampNow();
quint64 sinceLastQuery = now - _lastQueriedTime;
const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND;
bool queryIsDue = sinceLastQuery > TOO_LONG_SINCE_LAST_QUERY;
bool viewIsDifferentEnough = !_lastQueriedViewFrustum.isVerySimilar(_viewFrustum);
// if it's been a while since our last query or the view has significantly changed then send a query, otherwise suppress it
if (queryIsDue || viewIsDifferentEnough) {
_lastQueriedTime = now;
queryOctree(NodeType::VoxelServer, PacketTypeVoxelQuery, _voxelServerJurisdictions);
queryOctree(NodeType::ParticleServer, PacketTypeParticleQuery, _particleServerJurisdictions);
queryOctree(NodeType::ModelServer, PacketTypeModelQuery, _modelServerJurisdictions);
_lastQueriedViewFrustum = _viewFrustum;
// if it's been a while since our last query or the view has significantly changed then send a query, otherwise suppress it
if (queryIsDue || viewIsDifferentEnough) {
_lastQueriedTime = now;
queryOctree(NodeType::VoxelServer, PacketTypeVoxelQuery, _voxelServerJurisdictions);
queryOctree(NodeType::ParticleServer, PacketTypeParticleQuery, _particleServerJurisdictions);
queryOctree(NodeType::ModelServer, PacketTypeModelQuery, _modelServerJurisdictions);
_lastQueriedViewFrustum = _viewFrustum;
}
}
}
@ -2253,99 +2317,122 @@ glm::vec3 Application::getSunDirection() {
}
void Application::updateShadowMap() {
PerformanceTimer perfTimer("paintGL/updateShadowMap");
QOpenGLFramebufferObject* fbo = _textureCache.getShadowFramebufferObject();
fbo->bind();
glEnable(GL_DEPTH_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0, 0, fbo->width(), fbo->height());
glm::vec3 lightDirection = -getSunDirection();
glm::quat rotation = rotationBetween(IDENTITY_FRONT, lightDirection);
glm::quat inverseRotation = glm::inverse(rotation);
float nearScale = 0.0f;
const float MAX_SHADOW_DISTANCE = 2.0f;
float farScale = (MAX_SHADOW_DISTANCE - _viewFrustum.getNearClip()) /
(_viewFrustum.getFarClip() - _viewFrustum.getNearClip());
const float SHADOW_MATRIX_DISTANCES[] = { 0.0f, 2.0f, 6.0f, 14.0f, 30.0f };
const glm::vec2 MAP_COORDS[] = { glm::vec2(0.0f, 0.0f), glm::vec2(0.5f, 0.0f),
glm::vec2(0.0f, 0.5f), glm::vec2(0.5f, 0.5f) };
float frustumScale = 1.0f / (_viewFrustum.getFarClip() - _viewFrustum.getNearClip());
loadViewFrustum(_myCamera, _viewFrustum);
glm::vec3 points[] = {
glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), nearScale),
glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), nearScale),
glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), nearScale),
glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), nearScale),
glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), farScale),
glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), farScale),
glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), farScale),
glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), farScale) };
glm::vec3 center;
for (size_t i = 0; i < sizeof(points) / sizeof(points[0]); i++) {
center += points[i];
}
center /= (float)(sizeof(points) / sizeof(points[0]));
float radius = 0.0f;
for (size_t i = 0; i < sizeof(points) / sizeof(points[0]); i++) {
radius = qMax(radius, glm::distance(points[i], center));
}
center = inverseRotation * center;
// to reduce texture "shimmer," move in texel increments
float texelSize = (2.0f * radius) / fbo->width();
center = glm::vec3(roundf(center.x / texelSize) * texelSize, roundf(center.y / texelSize) * texelSize,
roundf(center.z / texelSize) * texelSize);
int matrixCount = 1;
int targetSize = fbo->width();
float targetScale = 1.0f;
if (Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows)) {
matrixCount = CASCADED_SHADOW_MATRIX_COUNT;
targetSize = fbo->width() / 2;
targetScale = 0.5f;
}
for (int i = 0; i < matrixCount; i++) {
const glm::vec2& coord = MAP_COORDS[i];
glViewport(coord.s * fbo->width(), coord.t * fbo->height(), targetSize, targetSize);
float nearScale = SHADOW_MATRIX_DISTANCES[i] * frustumScale;
float farScale = SHADOW_MATRIX_DISTANCES[i + 1] * frustumScale;
glm::vec3 points[] = {
glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), nearScale),
glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), nearScale),
glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), nearScale),
glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), nearScale),
glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), farScale),
glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), farScale),
glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), farScale),
glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), farScale) };
glm::vec3 center;
for (size_t j = 0; j < sizeof(points) / sizeof(points[0]); j++) {
center += points[j];
}
center /= (float)(sizeof(points) / sizeof(points[0]));
float radius = 0.0f;
for (size_t j = 0; j < sizeof(points) / sizeof(points[0]); j++) {
radius = qMax(radius, glm::distance(points[j], center));
}
if (i < 3) {
const float RADIUS_SCALE = 0.5f;
_shadowDistances[i] = -glm::distance(_viewFrustum.getPosition(), center) - radius * RADIUS_SCALE;
}
center = inverseRotation * center;
// to reduce texture "shimmer," move in texel increments
float texelSize = (2.0f * radius) / targetSize;
center = glm::vec3(roundf(center.x / texelSize) * texelSize, roundf(center.y / texelSize) * texelSize,
roundf(center.z / texelSize) * texelSize);
glm::vec3 minima(center.x - radius, center.y - radius, center.z - radius);
glm::vec3 maxima(center.x + radius, center.y + radius, center.z + radius);
// stretch out our extents in z so that we get all of the avatars
minima.z -= _viewFrustum.getFarClip() * 0.5f;
maxima.z += _viewFrustum.getFarClip() * 0.5f;
// save the combined matrix for rendering
_shadowMatrices[i] = glm::transpose(glm::translate(glm::vec3(coord, 0.0f)) *
glm::scale(glm::vec3(targetScale, targetScale, 1.0f)) *
glm::translate(glm::vec3(0.5f, 0.5f, 0.5f)) * glm::scale(glm::vec3(0.5f, 0.5f, 0.5f)) *
glm::ortho(minima.x, maxima.x, minima.y, maxima.y, -maxima.z, -minima.z) * glm::mat4_cast(inverseRotation));
// update the shadow view frustum
_shadowViewFrustum.setPosition(rotation * ((minima + maxima) * 0.5f));
_shadowViewFrustum.setOrientation(rotation);
_shadowViewFrustum.setOrthographic(true);
_shadowViewFrustum.setWidth(maxima.x - minima.x);
_shadowViewFrustum.setHeight(maxima.y - minima.y);
_shadowViewFrustum.setNearClip(minima.z);
_shadowViewFrustum.setFarClip(maxima.z);
_shadowViewFrustum.setEyeOffsetPosition(glm::vec3());
_shadowViewFrustum.setEyeOffsetOrientation(glm::quat());
_shadowViewFrustum.calculate();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(minima.x, maxima.x, minima.y, maxima.y, -maxima.z, -minima.z);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glm::vec3 axis = glm::axis(inverseRotation);
glRotatef(glm::degrees(glm::angle(inverseRotation)), axis.x, axis.y, axis.z);
// store view matrix without translation, which we'll use for precision-sensitive objects
updateUntranslatedViewMatrix();
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(1.1f, 4.0f); // magic numbers courtesy http://www.eecs.berkeley.edu/~ravir/6160/papers/shadowmaps.ppt
_avatarManager.renderAvatars(Avatar::SHADOW_RENDER_MODE);
_particles.render(OctreeRenderer::SHADOW_RENDER_MODE);
_models.render(OctreeRenderer::SHADOW_RENDER_MODE);
glDisable(GL_POLYGON_OFFSET_FILL);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
}
glm::vec3 minima(center.x - radius, center.y - radius, center.z - radius);
glm::vec3 maxima(center.x + radius, center.y + radius, center.z + radius);
// stretch out our extents in z so that we get all of the avatars
minima.z -= _viewFrustum.getFarClip() * 0.5f;
maxima.z += _viewFrustum.getFarClip() * 0.5f;
// save the combined matrix for rendering
_shadowMatrix = glm::transpose(glm::translate(glm::vec3(0.5f, 0.5f, 0.5f)) * glm::scale(glm::vec3(0.5f, 0.5f, 0.5f)) *
glm::ortho(minima.x, maxima.x, minima.y, maxima.y, -maxima.z, -minima.z) * glm::mat4_cast(inverseRotation));
// update the shadow view frustum
_shadowViewFrustum.setPosition(rotation * ((minima + maxima) * 0.5f));
_shadowViewFrustum.setOrientation(rotation);
_shadowViewFrustum.setOrthographic(true);
_shadowViewFrustum.setWidth(maxima.x - minima.x);
_shadowViewFrustum.setHeight(maxima.y - minima.y);
_shadowViewFrustum.setNearClip(minima.z);
_shadowViewFrustum.setFarClip(maxima.z);
_shadowViewFrustum.setEyeOffsetPosition(glm::vec3());
_shadowViewFrustum.setEyeOffsetOrientation(glm::quat());
_shadowViewFrustum.calculate();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(minima.x, maxima.x, minima.y, maxima.y, -maxima.z, -minima.z);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glm::vec3 axis = glm::axis(inverseRotation);
glRotatef(glm::degrees(glm::angle(inverseRotation)), axis.x, axis.y, axis.z);
// store view matrix without translation, which we'll use for precision-sensitive objects
updateUntranslatedViewMatrix();
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(1.1f, 4.0f); // magic numbers courtesy http://www.eecs.berkeley.edu/~ravir/6160/papers/shadowmaps.ppt
_avatarManager.renderAvatars(Avatar::SHADOW_RENDER_MODE);
_particles.render(OctreeRenderer::SHADOW_RENDER_MODE);
_models.render(OctreeRenderer::SHADOW_RENDER_MODE);
glDisable(GL_POLYGON_OFFSET_FILL);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
fbo->release();
glViewport(0, 0, _glWidget->width(), _glWidget->height());
@ -2392,6 +2479,7 @@ QImage Application::renderAvatarBillboard() {
}
void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
PerformanceTimer perfTimer("paintGL/displaySide");
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide()");
// transform by eye offset
@ -2424,9 +2512,27 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
glTranslatef(_viewMatrixTranslation.x, _viewMatrixTranslation.y, _viewMatrixTranslation.z);
// Setup 3D lights (after the camera transform, so that they are positioned in world space)
setupWorldLight();
{
PerformanceTimer perfTimer("paintGL/displaySide/setupWorldLight");
setupWorldLight();
}
// setup shadow matrices (again, after the camera transform)
int shadowMatrixCount = 0;
if (Menu::getInstance()->isOptionChecked(MenuOption::SimpleShadows)) {
shadowMatrixCount = 1;
} else if (Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows)) {
shadowMatrixCount = CASCADED_SHADOW_MATRIX_COUNT;
}
for (int i = shadowMatrixCount - 1; i >= 0; i--) {
glActiveTexture(GL_TEXTURE0 + i);
glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&_shadowMatrices[i][0]);
glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&_shadowMatrices[i][1]);
glTexGenfv(GL_R, GL_EYE_PLANE, (const GLfloat*)&_shadowMatrices[i][2]);
}
if (!selfAvatarOnly && Menu::getInstance()->isOptionChecked(MenuOption::Stars)) {
PerformanceTimer perfTimer("paintGL/displaySide/stars");
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::displaySide() ... stars...");
if (!_stars.isStarsLoaded()) {
@ -2455,6 +2561,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
// draw the sky dome
if (!selfAvatarOnly && Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) {
PerformanceTimer perfTimer("paintGL/displaySide/atmosphere");
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::displaySide() ... atmosphere...");
_environment.renderAtmospheres(whichCamera);
@ -2474,10 +2581,14 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
glMaterialfv(GL_FRONT, GL_SPECULAR, NO_SPECULAR_COLOR);
// draw the audio reflector overlay
_audioReflector.render();
{
PerformanceTimer perfTimer("paintGL/displaySide/audioReflector");
_audioReflector.render();
}
// Draw voxels
if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) {
PerformanceTimer perfTimer("paintGL/displaySide/voxels");
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::displaySide() ... voxels...");
_voxels.render();
@ -2485,12 +2596,14 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
// also, metavoxels
if (Menu::getInstance()->isOptionChecked(MenuOption::Metavoxels)) {
PerformanceTimer perfTimer("paintGL/displaySide/metavoxels");
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::displaySide() ... metavoxels...");
_metavoxels.render();
}
if (Menu::getInstance()->isOptionChecked(MenuOption::BuckyBalls)) {
PerformanceTimer perfTimer("paintGL/displaySide/buckyBalls");
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::displaySide() ... bucky balls...");
_buckyBalls.render();
@ -2498,6 +2611,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
// render particles...
if (Menu::getInstance()->isOptionChecked(MenuOption::Particles)) {
PerformanceTimer perfTimer("paintGL/displaySide/particles");
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::displaySide() ... particles...");
_particles.render();
@ -2505,6 +2619,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
// render models...
if (Menu::getInstance()->isOptionChecked(MenuOption::Models)) {
PerformanceTimer perfTimer("paintGL/displaySide/models");
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::displaySide() ... models...");
_models.render();
@ -2512,6 +2627,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
// render the ambient occlusion effect if enabled
if (Menu::getInstance()->isOptionChecked(MenuOption::AmbientOcclusion)) {
PerformanceTimer perfTimer("paintGL/displaySide/AmbientOcclusion");
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::displaySide() ... AmbientOcclusion...");
_ambientOcclusionEffect.render();
@ -2525,16 +2641,21 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
}
bool mirrorMode = (whichCamera.getInterpolatedMode() == CAMERA_MODE_MIRROR);
_avatarManager.renderAvatars(mirrorMode ? Avatar::MIRROR_RENDER_MODE : Avatar::NORMAL_RENDER_MODE, selfAvatarOnly);
{
PerformanceTimer perfTimer("paintGL/displaySide/renderAvatars");
_avatarManager.renderAvatars(mirrorMode ? Avatar::MIRROR_RENDER_MODE : Avatar::NORMAL_RENDER_MODE, selfAvatarOnly);
}
if (!selfAvatarOnly) {
// Render the world box
if (whichCamera.getMode() != CAMERA_MODE_MIRROR && Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
PerformanceTimer perfTimer("paintGL/displaySide/renderWorldBox");
renderWorldBox();
}
// brad's frustum for debugging
// view frustum for debugging
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayFrustum) && whichCamera.getMode() != CAMERA_MODE_MIRROR) {
PerformanceTimer perfTimer("paintGL/displaySide/ViewFrustum");
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::displaySide() ... renderViewFrustum...");
renderViewFrustum(_viewFrustum);
@ -2542,6 +2663,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
// render voxel fades if they exist
if (_voxelFades.size() > 0) {
PerformanceTimer perfTimer("paintGL/displaySide/voxel fades");
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::displaySide() ... voxel fades...");
_voxelFadesLock.lockForWrite();
@ -2557,10 +2679,16 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
}
// give external parties a change to hook in
emit renderingInWorldInterface();
{
PerformanceTimer perfTimer("paintGL/displaySide/inWorldInterface");
emit renderingInWorldInterface();
}
// render JS/scriptable overlays
_overlays.render3D();
{
PerformanceTimer perfTimer("paintGL/displaySide/3dOverlays");
_overlays.render3D();
}
}
}

View file

@ -261,11 +261,11 @@ public:
/// result from matrix multiplication at high translation magnitudes.
void loadTranslatedViewMatrix(const glm::vec3& translation);
const glm::mat4& getShadowMatrix() const { return _shadowMatrix; }
void getModelViewMatrix(glm::dmat4* modelViewMatrix);
void getProjectionMatrix(glm::dmat4* projectionMatrix);
const glm::vec3& getShadowDistances() const { return _shadowDistances; }
/// Computes the off-axis frustum parameters for the view frustum, taking mirroring into account.
void computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal,
float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const;
@ -492,7 +492,9 @@ private:
float _rotateMirror;
float _raiseMirror;
glm::mat4 _shadowMatrix;
static const int CASCADED_SHADOW_MATRIX_COUNT = 4;
glm::mat4 _shadowMatrices[CASCADED_SHADOW_MATRIX_COUNT];
glm::vec3 _shadowDistances;
Environment _environment;

View file

@ -315,7 +315,12 @@ Menu::Menu() :
appInstance->getGlowEffect(),
SLOT(cycleRenderMode()));
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Shadows, 0, false);
QMenu* shadowMenu = renderOptionsMenu->addMenu("Shadows");
QActionGroup* shadowGroup = new QActionGroup(shadowMenu);
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, "None", 0, true));
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::SimpleShadows, 0, false));
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::CascadedShadows, 0, false));
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Metavoxels, 0, true);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::BuckyBalls, 0, false);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Particles, 0, true);
@ -386,8 +391,18 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlternateIK, 0, false);
addDisabledActionAndSeparator(developerMenu, "Testing");
QMenu* timingMenu = developerMenu->addMenu("Timing and Statistics Tools");
QMenu* perfTimerMenu = timingMenu->addMenu("Performance Timer");
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayTimingDetails, 0, true);
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandDisplaySideTiming, 0, false);
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandAvatarSimulateTiming, 0, false);
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandAvatarUpdateTiming, 0, false);
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMiscAvatarTiming, 0, false);
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandIdleTiming, 0, false);
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandPaintGLTiming, 0, false);
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandUpdateTiming, 0, false);
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::TestPing, 0, true);
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::FrameTimer);
addActionToQMenuAndActionHash(timingMenu, MenuOption::RunTimingTests, 0, this, SLOT(runTests()));
@ -678,6 +693,10 @@ void Menu::scanMenu(QMenu* menu, settingsAction modifySetting, QSettings* set) {
set->endGroup();
}
bool Menu::getShadowsEnabled() const {
return isOptionChecked(MenuOption::SimpleShadows) || isOptionChecked(MenuOption::CascadedShadows);
}
void Menu::handleViewFrustumOffsetKeyModifier(int key) {
const float VIEW_FRUSTUM_OFFSET_DELTA = 0.5f;
const float VIEW_FRUSTUM_OFFSET_UP_DELTA = 0.05f;
@ -848,8 +867,8 @@ void Menu::setIsOptionChecked(const QString& menuOption, bool isChecked) {
}
}
bool Menu::isOptionChecked(const QString& menuOption) {
QAction* menu = _actionHash.value(menuOption);
bool Menu::isOptionChecked(const QString& menuOption) const {
const QAction* menu = _actionHash.value(menuOption);
if (menu) {
return menu->isChecked();
}

View file

@ -103,6 +103,8 @@ public:
int getMaxVoxels() const { return _maxVoxels; }
QAction* getUseVoxelShader() const { return _useVoxelShader; }
bool getShadowsEnabled() const;
void handleViewFrustumOffsetKeyModifier(int key);
// User Tweakable LOD Items
@ -171,7 +173,7 @@ public slots:
void removeSeparator(const QString& menuName, const QString& separatorName);
void addMenuItem(const MenuItemProperties& properties);
void removeMenuItem(const QString& menuName, const QString& menuitem);
bool isOptionChecked(const QString& menuOption);
bool isOptionChecked(const QString& menuOption) const;
void setIsOptionChecked(const QString& menuOption, bool isChecked);
private slots:
@ -304,6 +306,7 @@ namespace MenuOption {
const QString Bandwidth = "Bandwidth Display";
const QString BandwidthDetails = "Bandwidth Details";
const QString BuckyBalls = "Bucky Balls";
const QString CascadedShadows = "Cascaded";
const QString Chat = "Chat...";
const QString ChatCircling = "Chat Circling";
const QString CollideWithAvatars = "Collide With Avatars";
@ -321,10 +324,18 @@ namespace MenuOption {
const QString DisplayModelBounds = "Display Model Bounds";
const QString DisplayModelElementProxy = "Display Model Element Bounds";
const QString DisplayModelElementChildProxies = "Display Model Element Children";
const QString DisplayTimingDetails = "Display Timing Details";
const QString DontFadeOnVoxelServerChanges = "Don't Fade In/Out on Voxel Server Changes";
const QString EchoLocalAudio = "Echo Local Audio";
const QString EchoServerAudio = "Echo Server Audio";
const QString Enable3DTVMode = "Enable 3DTV Mode";
const QString ExpandMiscAvatarTiming = "Expand Misc MyAvatar Timing";
const QString ExpandAvatarUpdateTiming = "Expand MyAvatar update Timing";
const QString ExpandAvatarSimulateTiming = "Expand MyAvatar simulate Timing";
const QString ExpandDisplaySideTiming = "Expand Display Side Timing";
const QString ExpandIdleTiming = "Expand Idle Timing";
const QString ExpandPaintGLTiming = "Expand PaintGL Timing";
const QString ExpandUpdateTiming = "Expand Update Timing";
const QString Faceplus = "Faceplus";
const QString Faceshift = "Faceshift";
const QString FilterSixense = "Smooth Sixense Movement";
@ -380,7 +391,7 @@ namespace MenuOption {
const QString ScriptEditor = "Script Editor...";
const QString SettingsExport = "Export Settings";
const QString SettingsImport = "Import Settings";
const QString Shadows = "Shadows";
const QString SimpleShadows = "Simple";
const QString ShowBordersVoxelNodes = "Show Voxel Nodes";
const QString ShowBordersModelNodes = "Show Model Nodes";
const QString ShowBordersParticleNodes = "Show Particle Nodes";

View file

@ -378,10 +378,10 @@ void Avatar::simulateAttachments(float deltaTime) {
model->setLODDistance(getLODDistance());
}
if (_skeletonModel.getJointPosition(jointIndex, jointPosition) &&
_skeletonModel.getJointRotation(jointIndex, jointRotation)) {
_skeletonModel.getJointCombinedRotation(jointIndex, jointRotation)) {
model->setTranslation(jointPosition + jointRotation * attachment.translation * _scale);
model->setRotation(jointRotation * attachment.rotation);
model->setScale(_skeletonModel.getScale() * attachment.scale);
model->setScaleToFit(true, _scale * attachment.scale);
model->simulate(deltaTime);
}
}
@ -705,6 +705,54 @@ QStringList Avatar::getJointNames() const {
return _skeletonModel.isActive() ? _skeletonModel.getGeometry()->getFBXGeometry().getJointNames() : QStringList();
}
glm::vec3 Avatar::getJointPosition(int index) const {
if (QThread::currentThread() != thread()) {
glm::vec3 position;
QMetaObject::invokeMethod(const_cast<Avatar*>(this), "getJointPosition", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(glm::vec3, position), Q_ARG(const int, index));
return position;
}
glm::vec3 position;
_skeletonModel.getJointPosition(index, position);
return position;
}
glm::vec3 Avatar::getJointPosition(const QString& name) const {
if (QThread::currentThread() != thread()) {
glm::vec3 position;
QMetaObject::invokeMethod(const_cast<Avatar*>(this), "getJointPosition", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(glm::vec3, position), Q_ARG(const QString&, name));
return position;
}
glm::vec3 position;
_skeletonModel.getJointPosition(getJointIndex(name), position);
return position;
}
glm::quat Avatar::getJointCombinedRotation(int index) const {
if (QThread::currentThread() != thread()) {
glm::quat rotation;
QMetaObject::invokeMethod(const_cast<Avatar*>(this), "getJointCombinedRotation", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(glm::quat, rotation), Q_ARG(const int, index));
return rotation;
}
glm::quat rotation;
_skeletonModel.getJointCombinedRotation(index, rotation);
return rotation;
}
glm::quat Avatar::getJointCombinedRotation(const QString& name) const {
if (QThread::currentThread() != thread()) {
glm::quat rotation;
QMetaObject::invokeMethod(const_cast<Avatar*>(this), "getJointCombinedRotation", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(glm::quat, rotation), Q_ARG(const QString&, name));
return rotation;
}
glm::quat rotation;
_skeletonModel.getJointCombinedRotation(getJointIndex(name), rotation);
return rotation;
}
void Avatar::setFaceModelURL(const QUrl& faceModelURL) {
AvatarData::setFaceModelURL(faceModelURL);
const QUrl DEFAULT_FACE_MODEL_URL = QUrl::fromLocalFile(Application::resourcesPath() + "meshes/defaultAvatar_head.fst");
@ -734,6 +782,8 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
// update the urls
for (int i = 0; i < attachmentData.size(); i++) {
_attachmentModels[i]->setSnapModelToCenter(true);
_attachmentModels[i]->setScaleToFit(true, _scale * _attachmentData.at(i).scale);
_attachmentModels[i]->setURL(attachmentData.at(i).modelURL);
}
}

View file

@ -152,7 +152,12 @@ public:
quint32 getCollisionGroups() const { return _collisionGroups; }
virtual void setCollisionGroups(quint32 collisionGroups) { _collisionGroups = (collisionGroups & VALID_COLLISION_GROUPS); }
Q_INVOKABLE glm::vec3 getJointPosition(int index) const;
Q_INVOKABLE glm::vec3 getJointPosition(const QString& name) const;
Q_INVOKABLE glm::quat getJointCombinedRotation(int index) const;
Q_INVOKABLE glm::quat getJointCombinedRotation(const QString& name) const;
public slots:
void updateCollisionGroups();

View file

@ -24,9 +24,9 @@
#include <GeometryUtil.h>
#include <NodeList.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include <PerfStat.h>
#include <ShapeCollider.h>
#include <SharedUtil.h>
#include "Application.h"
#include "Audio.h"
@ -76,8 +76,7 @@ MyAvatar::MyAvatar() :
_lastFloorContactPoint(0.0f),
_lookAtTargetAvatar(),
_shouldRender(true),
_billboardValid(false),
_oculusYawOffset(0.0f)
_billboardValid(false)
{
for (int i = 0; i < MAX_DRIVE_KEYS; i++) {
_driveKeys[i] = 0.0f;
@ -90,8 +89,7 @@ MyAvatar::~MyAvatar() {
void MyAvatar::reset() {
_skeletonModel.reset();
getHead()->reset();
_oculusYawOffset = 0.0f;
getHead()->reset();
setVelocity(glm::vec3(0.0f));
setThrust(glm::vec3(0.0f));
@ -103,10 +101,15 @@ void MyAvatar::reset() {
}
void MyAvatar::update(float deltaTime) {
PerformanceTimer perfTimer("MyAvatar::update/");
Head* head = getHead();
head->relaxLean(deltaTime);
updateFromTrackers(deltaTime);
{
PerformanceTimer perfTimer("MyAvatar::update/updateFromTrackers");
updateFromTrackers(deltaTime);
}
if (Menu::getInstance()->isOptionChecked(MenuOption::MoveWithLean)) {
PerformanceTimer perfTimer("MyAvatar::update/moveWithLean");
// Faceshift drive is enabled, set the avatar drive based on the head position
moveWithLean();
}
@ -117,13 +120,18 @@ void MyAvatar::update(float deltaTime) {
head->setAudioAverageLoudness(audio->getAudioAverageInputLoudness());
if (_motionBehaviors & AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY) {
PerformanceTimer perfTimer("MyAvatar::update/gravityWork");
setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition()));
}
simulate(deltaTime);
{
PerformanceTimer perfTimer("MyAvatar::update/simulate");
simulate(deltaTime);
}
}
void MyAvatar::simulate(float deltaTime) {
PerformanceTimer perfTimer("MyAvatar::simulate");
if (_scale != _targetScale) {
float scale = (1.0f - SMOOTHING_RATIO) * _scale + SMOOTHING_RATIO * _targetScale;
@ -134,34 +142,56 @@ void MyAvatar::simulate(float deltaTime) {
// no extra movement of the hand here any more ...
_handState = HAND_STATE_NULL;
updateOrientation(deltaTime);
updatePosition(deltaTime);
// update avatar skeleton and simulate hand and head
getHand()->collideAgainstOurself();
getHand()->simulate(deltaTime, true);
_skeletonModel.simulate(deltaTime);
simulateAttachments(deltaTime);
// copy out the skeleton joints from the model
_jointData.resize(_skeletonModel.getJointStateCount());
for (int i = 0; i < _jointData.size(); i++) {
JointData& data = _jointData[i];
data.valid = _skeletonModel.getJointState(i, data.rotation);
{
PerformanceTimer perfTimer("MyAvatar::simulate/updateOrientation");
updateOrientation(deltaTime);
}
{
PerformanceTimer perfTimer("MyAvatar::simulate/updatePosition");
updatePosition(deltaTime);
}
Head* head = getHead();
glm::vec3 headPosition;
if (!_skeletonModel.getHeadPosition(headPosition)) {
headPosition = _position;
{
PerformanceTimer perfTimer("MyAvatar::simulate/hand Collision,simulate");
// update avatar skeleton and simulate hand and head
getHand()->collideAgainstOurself();
getHand()->simulate(deltaTime, true);
}
{
PerformanceTimer perfTimer("MyAvatar::simulate/_skeletonModel.simulate()");
_skeletonModel.simulate(deltaTime);
}
{
PerformanceTimer perfTimer("MyAvatar::simulate/simulateAttachments");
simulateAttachments(deltaTime);
}
{
PerformanceTimer perfTimer("MyAvatar::simulate/copy joints");
// copy out the skeleton joints from the model
_jointData.resize(_skeletonModel.getJointStateCount());
for (int i = 0; i < _jointData.size(); i++) {
JointData& data = _jointData[i];
data.valid = _skeletonModel.getJointState(i, data.rotation);
}
}
{
PerformanceTimer perfTimer("MyAvatar::simulate/head Simulate");
Head* head = getHead();
glm::vec3 headPosition;
if (!_skeletonModel.getHeadPosition(headPosition)) {
headPosition = _position;
}
head->setPosition(headPosition);
head->setScale(_scale);
head->simulate(deltaTime, true);
}
head->setPosition(headPosition);
head->setScale(_scale);
head->simulate(deltaTime, true);
// now that we're done stepping the avatar forward in time, compute new collisions
if (_collisionGroups != 0) {
PerformanceTimer perfTimer("MyAvatar::simulate/_collisionGroups");
Camera* myCamera = Application::getInstance()->getCamera();
float radius = getSkeletonHeight() * COLLISION_RADIUS_SCALE;
@ -171,14 +201,17 @@ void MyAvatar::simulate(float deltaTime) {
}
updateShapePositions();
if (_collisionGroups & COLLISION_GROUP_ENVIRONMENT) {
PerformanceTimer perfTimer("MyAvatar::simulate/updateCollisionWithEnvironment");
updateCollisionWithEnvironment(deltaTime, radius);
}
if (_collisionGroups & COLLISION_GROUP_VOXELS) {
PerformanceTimer perfTimer("MyAvatar::simulate/updateCollisionWithVoxels");
updateCollisionWithVoxels(deltaTime, radius);
} else {
_trapDuration = 0.0f;
}
if (_collisionGroups & COLLISION_GROUP_AVATARS) {
PerformanceTimer perfTimer("MyAvatar::simulate/updateCollisionWithAvatars");
updateCollisionWithAvatars(deltaTime);
}
}
@ -791,6 +824,7 @@ bool MyAvatar::shouldRenderHead(const glm::vec3& cameraPosition, RenderMode rend
}
float MyAvatar::computeDistanceToFloor(const glm::vec3& startPoint) {
PerformanceTimer perfTimer("MyAvatar::computeDistanceToFloor()");
glm::vec3 direction = -_worldUpDirection;
OctreeElement* elementHit; // output from findRayIntersection
float distance = FLT_MAX; // output from findRayIntersection
@ -828,43 +862,8 @@ void MyAvatar::updateOrientation(float deltaTime) {
OculusManager::getEulerAngles(yaw, pitch, roll);
// ... so they need to be converted to degrees before we do math...
// The neck is limited in how much it can yaw, so we check its relative
// yaw from the body and yaw the body if necessary.
yaw *= DEGREES_PER_RADIAN;
float bodyToHeadYaw = yaw - _oculusYawOffset;
const float MAX_NECK_YAW = 85.0f; // degrees
if ((fabs(bodyToHeadYaw) > 2.0f * MAX_NECK_YAW) && (yaw * _oculusYawOffset < 0.0f)) {
// We've wrapped around the range for yaw so adjust
// the measured yaw to be relative to _oculusYawOffset.
if (yaw > 0.0f) {
yaw -= 360.0f;
} else {
yaw += 360.0f;
}
bodyToHeadYaw = yaw - _oculusYawOffset;
}
float delta = fabs(bodyToHeadYaw) - MAX_NECK_YAW;
if (delta > 0.0f) {
yaw = MAX_NECK_YAW;
if (bodyToHeadYaw < 0.0f) {
delta *= -1.0f;
bodyToHeadYaw = -MAX_NECK_YAW;
} else {
bodyToHeadYaw = MAX_NECK_YAW;
}
// constrain _oculusYawOffset to be within range [-180,180]
_oculusYawOffset = fmod((_oculusYawOffset + delta) + 180.0f, 360.0f) - 180.0f;
// We must adjust the body orientation using a delta rotation (rather than
// doing yaw math) because the body's yaw ranges are not the same
// as what the Oculus API provides.
glm::quat bodyCorrection = glm::angleAxis(glm::radians(delta), _worldUpDirection);
orientation = orientation * bodyCorrection;
}
Head* head = getHead();
head->setBaseYaw(bodyToHeadYaw);
head->setBaseYaw(yaw * DEGREES_PER_RADIAN);
head->setBasePitch(pitch * DEGREES_PER_RADIAN);
head->setBaseRoll(roll * DEGREES_PER_RADIAN);
}
@ -876,6 +875,7 @@ void MyAvatar::updateOrientation(float deltaTime) {
const float NEARBY_FLOOR_THRESHOLD = 5.0f;
void MyAvatar::updatePosition(float deltaTime) {
PerformanceTimer perfTimer("MyAvatar::updatePosition");
float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) +
fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT]) +
fabsf(_driveKeys[UP] - _driveKeys[DOWN]);

View file

@ -14,6 +14,8 @@
#include <QOpenGLFramebufferObject>
#include <PerfStat.h>
#include "Application.h"
#include "GlowEffect.h"
#include "ProgramObject.h"
@ -119,6 +121,8 @@ static void maybeRelease(QOpenGLFramebufferObject* fbo) {
}
QOpenGLFramebufferObject* GlowEffect::render(bool toTexture) {
PerformanceTimer perfTimer("paintGL/glowEffect");
QOpenGLFramebufferObject* primaryFBO = Application::getInstance()->getTextureCache()->getPrimaryFramebufferObject();
primaryFBO->release();
glBindTexture(GL_TEXTURE_2D, primaryFBO->texture());

View file

@ -65,6 +65,11 @@ ProgramObject Model::_shadowNormalMapProgram;
ProgramObject Model::_shadowSpecularMapProgram;
ProgramObject Model::_shadowNormalSpecularMapProgram;
ProgramObject Model::_cascadedShadowMapProgram;
ProgramObject Model::_cascadedShadowNormalMapProgram;
ProgramObject Model::_cascadedShadowSpecularMapProgram;
ProgramObject Model::_cascadedShadowNormalSpecularMapProgram;
ProgramObject Model::_shadowProgram;
ProgramObject Model::_skinProgram;
@ -77,13 +82,25 @@ ProgramObject Model::_skinShadowNormalMapProgram;
ProgramObject Model::_skinShadowSpecularMapProgram;
ProgramObject Model::_skinShadowNormalSpecularMapProgram;
ProgramObject Model::_skinCascadedShadowMapProgram;
ProgramObject Model::_skinCascadedShadowNormalMapProgram;
ProgramObject Model::_skinCascadedShadowSpecularMapProgram;
ProgramObject Model::_skinCascadedShadowNormalSpecularMapProgram;
ProgramObject Model::_skinShadowProgram;
int Model::_normalMapTangentLocation;
int Model::_normalSpecularMapTangentLocation;
int Model::_shadowNormalMapTangentLocation;
int Model::_shadowNormalSpecularMapTangentLocation;
int Model::_cascadedShadowNormalMapTangentLocation;
int Model::_cascadedShadowNormalSpecularMapTangentLocation;
int Model::_cascadedShadowMapDistancesLocation;
int Model::_cascadedShadowNormalMapDistancesLocation;
int Model::_cascadedShadowSpecularMapDistancesLocation;
int Model::_cascadedShadowNormalSpecularMapDistancesLocation;
Model::SkinLocations Model::_skinLocations;
Model::SkinLocations Model::_skinNormalMapLocations;
Model::SkinLocations Model::_skinSpecularMapLocations;
@ -92,6 +109,10 @@ Model::SkinLocations Model::_skinShadowMapLocations;
Model::SkinLocations Model::_skinShadowNormalMapLocations;
Model::SkinLocations Model::_skinShadowSpecularMapLocations;
Model::SkinLocations Model::_skinShadowNormalSpecularMapLocations;
Model::SkinLocations Model::_skinCascadedShadowMapLocations;
Model::SkinLocations Model::_skinCascadedShadowNormalMapLocations;
Model::SkinLocations Model::_skinCascadedShadowSpecularMapLocations;
Model::SkinLocations Model::_skinCascadedShadowNormalSpecularMapLocations;
Model::SkinLocations Model::_skinShadowLocations;
void Model::setScale(const glm::vec3& scale) {
@ -128,6 +149,7 @@ void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locati
locations.clusterIndices = program.attributeLocation("clusterIndices");
locations.clusterWeights = program.attributeLocation("clusterWeights");
locations.tangent = program.attributeLocation("tangent");
locations.shadowDistances = program.uniformLocation("shadowDistances");
program.setUniformValue("diffuseMap", 0);
program.setUniformValue("normalMap", 1);
program.setUniformValue("specularMap", specularTextureUnit);
@ -222,7 +244,7 @@ void Model::init() {
_normalSpecularMapProgram.setUniformValue("diffuseMap", 0);
_normalSpecularMapProgram.setUniformValue("normalMap", 1);
_normalSpecularMapProgram.setUniformValue("specularMap", 2);
_normalSpecularMapTangentLocation = _normalMapProgram.attributeLocation("tangent");
_normalSpecularMapTangentLocation = _normalSpecularMapProgram.attributeLocation("tangent");
_normalSpecularMapProgram.release();
@ -272,10 +294,66 @@ void Model::init() {
_shadowNormalSpecularMapProgram.setUniformValue("normalMap", 1);
_shadowNormalSpecularMapProgram.setUniformValue("specularMap", 2);
_shadowNormalSpecularMapProgram.setUniformValue("shadowMap", 3);
_shadowNormalSpecularMapTangentLocation = _normalMapProgram.attributeLocation("tangent");
_shadowNormalSpecularMapTangentLocation = _shadowNormalSpecularMapProgram.attributeLocation("tangent");
_shadowNormalSpecularMapProgram.release();
_cascadedShadowMapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() +
"shaders/model.vert");
_cascadedShadowMapProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() +
"shaders/model_cascaded_shadow_map.frag");
_cascadedShadowMapProgram.link();
_cascadedShadowMapProgram.bind();
_cascadedShadowMapProgram.setUniformValue("diffuseMap", 0);
_cascadedShadowMapProgram.setUniformValue("shadowMap", 1);
_cascadedShadowMapDistancesLocation = _cascadedShadowMapProgram.uniformLocation("shadowDistances");
_cascadedShadowMapProgram.release();
_cascadedShadowNormalMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
Application::resourcesPath() + "shaders/model_normal_map.vert");
_cascadedShadowNormalMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/model_cascaded_shadow_normal_map.frag");
_cascadedShadowNormalMapProgram.link();
_cascadedShadowNormalMapProgram.bind();
_cascadedShadowNormalMapProgram.setUniformValue("diffuseMap", 0);
_cascadedShadowNormalMapProgram.setUniformValue("normalMap", 1);
_cascadedShadowNormalMapProgram.setUniformValue("shadowMap", 2);
_cascadedShadowNormalMapDistancesLocation = _cascadedShadowNormalMapProgram.uniformLocation("shadowDistances");
_cascadedShadowNormalMapTangentLocation = _cascadedShadowNormalMapProgram.attributeLocation("tangent");
_cascadedShadowNormalMapProgram.release();
_cascadedShadowSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
Application::resourcesPath() + "shaders/model.vert");
_cascadedShadowSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/model_cascaded_shadow_specular_map.frag");
_cascadedShadowSpecularMapProgram.link();
_cascadedShadowSpecularMapProgram.bind();
_cascadedShadowSpecularMapProgram.setUniformValue("diffuseMap", 0);
_cascadedShadowSpecularMapProgram.setUniformValue("specularMap", 1);
_cascadedShadowSpecularMapProgram.setUniformValue("shadowMap", 2);
_cascadedShadowSpecularMapDistancesLocation = _cascadedShadowSpecularMapProgram.uniformLocation("shadowDistances");
_cascadedShadowSpecularMapProgram.release();
_cascadedShadowNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
Application::resourcesPath() + "shaders/model_normal_map.vert");
_cascadedShadowNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/model_cascaded_shadow_normal_specular_map.frag");
_cascadedShadowNormalSpecularMapProgram.link();
_cascadedShadowNormalSpecularMapProgram.bind();
_cascadedShadowNormalSpecularMapProgram.setUniformValue("diffuseMap", 0);
_cascadedShadowNormalSpecularMapProgram.setUniformValue("normalMap", 1);
_cascadedShadowNormalSpecularMapProgram.setUniformValue("specularMap", 2);
_cascadedShadowNormalSpecularMapProgram.setUniformValue("shadowMap", 3);
_cascadedShadowNormalSpecularMapDistancesLocation =
_cascadedShadowNormalSpecularMapProgram.uniformLocation("shadowDistances");
_cascadedShadowNormalSpecularMapTangentLocation = _cascadedShadowNormalSpecularMapProgram.attributeLocation("tangent");
_cascadedShadowNormalSpecularMapProgram.release();
_shadowProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model_shadow.vert");
_shadowProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/model_shadow.frag");
@ -346,6 +424,39 @@ void Model::init() {
initSkinProgram(_skinShadowNormalSpecularMapProgram, _skinShadowNormalSpecularMapLocations, 2, 3);
_skinCascadedShadowMapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() +
"shaders/skin_model.vert");
_skinCascadedShadowMapProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() +
"shaders/model_cascaded_shadow_map.frag");
_skinCascadedShadowMapProgram.link();
initSkinProgram(_skinCascadedShadowMapProgram, _skinCascadedShadowMapLocations);
_skinCascadedShadowNormalMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
Application::resourcesPath() + "shaders/skin_model_normal_map.vert");
_skinCascadedShadowNormalMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/model_cascaded_shadow_normal_map.frag");
_skinCascadedShadowNormalMapProgram.link();
initSkinProgram(_skinCascadedShadowNormalMapProgram, _skinCascadedShadowNormalMapLocations, 1, 2);
_skinCascadedShadowSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
Application::resourcesPath() + "shaders/skin_model.vert");
_skinCascadedShadowSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/model_cascaded_shadow_specular_map.frag");
_skinCascadedShadowSpecularMapProgram.link();
initSkinProgram(_skinCascadedShadowSpecularMapProgram, _skinCascadedShadowSpecularMapLocations, 1, 2);
_skinCascadedShadowNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
Application::resourcesPath() + "shaders/skin_model_normal_map.vert");
_skinCascadedShadowNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/model_cascaded_shadow_normal_specular_map.frag");
_skinCascadedShadowNormalSpecularMapProgram.link();
initSkinProgram(_skinCascadedShadowNormalSpecularMapProgram, _skinCascadedShadowNormalSpecularMapLocations, 2, 3);
_skinShadowProgram.addShaderFromSourceFile(QGLShader::Vertex,
Application::resourcesPath() + "shaders/skin_model_shadow.vert");
_skinShadowProgram.addShaderFromSourceFile(QGLShader::Fragment,
@ -501,7 +612,7 @@ bool Model::render(float alpha, RenderMode mode, bool receiveShadows) {
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.5f * alpha);
receiveShadows &= Menu::getInstance()->isOptionChecked(MenuOption::Shadows);
receiveShadows &= Menu::getInstance()->getShadowsEnabled();
renderMeshes(alpha, mode, false, receiveShadows);
glDisable(GL_ALPHA_TEST);
@ -634,8 +745,16 @@ bool Model::getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind)
return false;
}
rotation = _jointStates[jointIndex]._combinedRotation *
(fromBind ? _geometry->getFBXGeometry().joints[jointIndex].inverseBindRotation :
_geometry->getFBXGeometry().joints[jointIndex].inverseDefaultRotation);
(fromBind ? _geometry->getFBXGeometry().joints[jointIndex].inverseBindRotation :
_geometry->getFBXGeometry().joints[jointIndex].inverseDefaultRotation);
return true;
}
bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const {
if (jointIndex == -1 || _jointStates.isEmpty()) {
return false;
}
rotation = _jointStates[jointIndex]._combinedRotation;
return true;
}
@ -1494,11 +1613,7 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re
const FBXGeometry& geometry = _geometry->getFBXGeometry();
const QVector<NetworkMesh>& networkMeshes = _geometry->getMeshes();
if (receiveShadows) {
glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[0]);
glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[1]);
glTexGenfv(GL_R, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[2]);
}
bool cascadedShadows = Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows);
for (int i = 0; i < networkMeshes.size(); i++) {
// exit early if the translucency doesn't match what we're drawing
const NetworkMesh& networkMesh = networkMeshes.at(i);
@ -1520,6 +1635,8 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re
ProgramObject* program = &_program;
ProgramObject* skinProgram = &_skinProgram;
SkinLocations* skinLocations = &_skinLocations;
int tangentLocation = _normalMapTangentLocation;
int shadowDistancesLocation = _cascadedShadowMapDistancesLocation;
GLenum specularTextureUnit = 0;
GLenum shadowTextureUnit = 0;
if (mode == SHADOW_RENDER_MODE) {
@ -1530,21 +1647,40 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re
} else if (!mesh.tangents.isEmpty()) {
if (mesh.hasSpecularTexture()) {
if (receiveShadows) {
program = &_shadowNormalSpecularMapProgram;
skinProgram = &_skinShadowNormalSpecularMapProgram;
skinLocations = &_skinShadowNormalSpecularMapLocations;
if (cascadedShadows) {
program = &_cascadedShadowNormalSpecularMapProgram;
skinProgram = &_skinCascadedShadowNormalSpecularMapProgram;
skinLocations = &_skinCascadedShadowNormalSpecularMapLocations;
tangentLocation = _cascadedShadowNormalSpecularMapTangentLocation;
shadowDistancesLocation = _cascadedShadowNormalSpecularMapDistancesLocation;
} else {
program = &_shadowNormalSpecularMapProgram;
skinProgram = &_skinShadowNormalSpecularMapProgram;
skinLocations = &_skinShadowNormalSpecularMapLocations;
tangentLocation = _shadowNormalSpecularMapTangentLocation;
}
shadowTextureUnit = GL_TEXTURE3;
} else {
program = &_normalSpecularMapProgram;
skinProgram = &_skinNormalSpecularMapProgram;
skinLocations = &_skinNormalSpecularMapLocations;
tangentLocation = _normalSpecularMapTangentLocation;
}
specularTextureUnit = GL_TEXTURE2;
} else if (receiveShadows) {
program = &_shadowNormalMapProgram;
skinProgram = &_skinShadowNormalMapProgram;
skinLocations = &_skinShadowNormalMapLocations;
if (cascadedShadows) {
program = &_cascadedShadowNormalMapProgram;
skinProgram = &_skinCascadedShadowNormalMapProgram;
skinLocations = &_skinCascadedShadowNormalMapLocations;
tangentLocation = _cascadedShadowNormalMapTangentLocation;
shadowDistancesLocation = _cascadedShadowNormalMapDistancesLocation;
} else {
program = &_shadowNormalMapProgram;
skinProgram = &_skinShadowNormalMapProgram;
skinLocations = &_skinShadowNormalMapLocations;
tangentLocation = _shadowNormalMapTangentLocation;
}
shadowTextureUnit = GL_TEXTURE2;
} else {
program = &_normalMapProgram;
@ -1553,9 +1689,16 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re
}
} else if (mesh.hasSpecularTexture()) {
if (receiveShadows) {
program = &_shadowSpecularMapProgram;
skinProgram = &_skinShadowSpecularMapProgram;
skinLocations = &_skinShadowSpecularMapLocations;
if (cascadedShadows) {
program = &_cascadedShadowSpecularMapProgram;
skinProgram = &_skinCascadedShadowSpecularMapProgram;
skinLocations = &_skinCascadedShadowSpecularMapLocations;
shadowDistancesLocation = _cascadedShadowSpecularMapDistancesLocation;
} else {
program = &_shadowSpecularMapProgram;
skinProgram = &_skinShadowSpecularMapProgram;
skinLocations = &_skinShadowSpecularMapLocations;
}
shadowTextureUnit = GL_TEXTURE2;
} else {
program = &_specularMapProgram;
@ -1565,15 +1708,20 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re
specularTextureUnit = GL_TEXTURE1;
} else if (receiveShadows) {
program = &_shadowMapProgram;
skinProgram = &_skinShadowMapProgram;
skinLocations = &_skinShadowMapLocations;
if (cascadedShadows) {
program = &_cascadedShadowMapProgram;
skinProgram = &_skinCascadedShadowMapProgram;
skinLocations = &_skinCascadedShadowMapLocations;
} else {
program = &_shadowMapProgram;
skinProgram = &_skinShadowMapProgram;
skinLocations = &_skinShadowMapLocations;
}
shadowTextureUnit = GL_TEXTURE1;
}
const MeshState& state = _meshStates.at(i);
ProgramObject* activeProgram = program;
int tangentLocation = _normalMapTangentLocation;
glPushMatrix();
Application::getInstance()->loadTranslatedViewMatrix(_translation);
@ -1591,10 +1739,15 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re
skinProgram->enableAttributeArray(skinLocations->clusterWeights);
activeProgram = skinProgram;
tangentLocation = skinLocations->tangent;
if (cascadedShadows) {
program->setUniform(skinLocations->shadowDistances, Application::getInstance()->getShadowDistances());
}
} else {
glMultMatrixf((const GLfloat*)&state.clusterMatrices[0]);
program->bind();
if (cascadedShadows) {
program->setUniform(shadowDistancesLocation, Application::getInstance()->getShadowDistances());
}
}
if (mesh.blendshapes.isEmpty()) {

View file

@ -144,6 +144,7 @@ public:
bool getJointPosition(int jointIndex, glm::vec3& position) const;
bool getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind = false) const;
bool getJointCombinedRotation(int jointIndex, glm::quat& rotation) const;
QStringList getJointNames() const;
@ -292,6 +293,11 @@ private:
static ProgramObject _shadowSpecularMapProgram;
static ProgramObject _shadowNormalSpecularMapProgram;
static ProgramObject _cascadedShadowMapProgram;
static ProgramObject _cascadedShadowNormalMapProgram;
static ProgramObject _cascadedShadowSpecularMapProgram;
static ProgramObject _cascadedShadowNormalSpecularMapProgram;
static ProgramObject _shadowProgram;
static ProgramObject _skinProgram;
@ -304,12 +310,24 @@ private:
static ProgramObject _skinShadowSpecularMapProgram;
static ProgramObject _skinShadowNormalSpecularMapProgram;
static ProgramObject _skinCascadedShadowMapProgram;
static ProgramObject _skinCascadedShadowNormalMapProgram;
static ProgramObject _skinCascadedShadowSpecularMapProgram;
static ProgramObject _skinCascadedShadowNormalSpecularMapProgram;
static ProgramObject _skinShadowProgram;
static int _normalMapTangentLocation;
static int _normalSpecularMapTangentLocation;
static int _shadowNormalMapTangentLocation;
static int _shadowNormalSpecularMapTangentLocation;
static int _cascadedShadowNormalMapTangentLocation;
static int _cascadedShadowNormalSpecularMapTangentLocation;
static int _cascadedShadowMapDistancesLocation;
static int _cascadedShadowNormalMapDistancesLocation;
static int _cascadedShadowSpecularMapDistancesLocation;
static int _cascadedShadowNormalSpecularMapDistancesLocation;
class SkinLocations {
public:
@ -317,6 +335,7 @@ private:
int clusterIndices;
int clusterWeights;
int tangent;
int shadowDistances;
};
static SkinLocations _skinLocations;
@ -327,6 +346,10 @@ private:
static SkinLocations _skinShadowNormalMapLocations;
static SkinLocations _skinShadowSpecularMapLocations;
static SkinLocations _skinShadowNormalSpecularMapLocations;
static SkinLocations _skinCascadedShadowMapLocations;
static SkinLocations _skinCascadedShadowNormalMapLocations;
static SkinLocations _skinCascadedShadowSpecularMapLocations;
static SkinLocations _skinCascadedShadowNormalSpecularMapLocations;
static SkinLocations _skinShadowLocations;
static void initSkinProgram(ProgramObject& program, SkinLocations& locations,

View file

@ -18,6 +18,8 @@
#include <glm/gtx/quaternion.hpp>
#include <glm/gtx/vector_angle.hpp>
#include <PerfStat.h>
#include "Stats.h"
#include "InterfaceConfig.h"
#include "Menu.h"
@ -158,6 +160,39 @@ void Stats::drawBackground(unsigned int rgba, int x, int y, int width, int heigh
glColor4f(1, 1, 1, 1);
}
bool Stats::includeTimingRecord(const QString& name) {
bool included = false;
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) {
if (name == "idle/update") {
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandUpdateTiming) ||
Menu::getInstance()->isOptionChecked(MenuOption::ExpandIdleTiming);
} else if (name == "idle/updateGL") {
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandIdleTiming);
} else if (name.startsWith("idle/update")) {
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandUpdateTiming);
} else if (name.startsWith("idle/")) {
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandIdleTiming);
} else if (name.startsWith("MyAvatar::simulate")) {
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandAvatarSimulateTiming);
} else if (name.startsWith("MyAvatar::update/") || name.startsWith("updateMyAvatar")) {
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandAvatarUpdateTiming);
} else if (name.startsWith("MyAvatar::")) {
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandMiscAvatarTiming);
} else if (name == "paintGL/displaySide") {
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandDisplaySideTiming) ||
Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming);
} else if (name.startsWith("paintGL/displaySide/")) {
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandDisplaySideTiming);
} else if (name.startsWith("paintGL/")) {
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming);
} else {
included = true; // include everything else
}
}
return included;
}
// display expanded or contracted stats
void Stats::display(
const float* color,
@ -345,11 +380,25 @@ void Stats::display(
VoxelSystem* voxels = Application::getInstance()->getVoxels();
lines = _expanded ? 12 : 3;
lines = _expanded ? 11 : 3;
if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessing)) {
lines += 9; // spatial audio processing adds 1 spacing line and 8 extra lines of info
}
if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) {
// we will also include room for 1 line per timing record and a header
lines += 1;
const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords();
QMapIterator<QString, PerformanceTimerRecord> i(allRecords);
while (i.hasNext()) {
i.next();
if (includeTimingRecord(i.key())) {
lines++;
}
}
}
drawBackground(backgroundColor, horizontalOffset, 0, glWidget->width() - horizontalOffset, lines * STATS_PELS_PER_LINE + 10);
horizontalOffset += 5;
@ -454,8 +503,6 @@ void Stats::display(
}
}
verticalOffset += (_expanded ? STATS_PELS_PER_LINE : 0);
QString serversTotalString = locale.toString((uint)totalNodes); // consider adding: .rightJustified(10, ' ');
// Server Voxels
@ -508,6 +555,29 @@ void Stats::display(
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
}
// TODO: the display of these timing details should all be moved to JavaScript
if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) {
// Timing details...
const int TIMER_OUTPUT_LINE_LENGTH = 300;
char perfLine[TIMER_OUTPUT_LINE_LENGTH];
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font,
"--------------------- Function -------------------- --msecs- -calls--", color);
const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords();
QMapIterator<QString, PerformanceTimerRecord> i(allRecords);
while (i.hasNext()) {
i.next();
if (includeTimingRecord(i.key())) {
sprintf(perfLine, "%50s: %8.4f [%6llu]", qPrintable(i.key()),
(float)i.value().getMovingAverage() / (float)USECS_PER_MSEC,
i.value().getCount());
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, perfLine, color);
}
}
}
if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessing)) {
verticalOffset += STATS_PELS_PER_LINE; // space one line...

View file

@ -30,6 +30,7 @@ public:
void checkClick(int mouseX, int mouseY, int mouseDragStartedX, int mouseDragStartedY, int horizontalOffset);
void resetWidth(int width, int horizontalOffset);
void display(const float* color, int horizontalOffset, float fps, int packetsPerSecond, int bytesPerSecond, int voxelPacketsToProcess);
bool includeTimingRecord(const QString& name);
private:
static Stats* _sharedInstance;

View file

@ -519,6 +519,17 @@ void VoxelSystem::initVoxelMemory() {
_shadowMapProgram.bind();
_shadowMapProgram.setUniformValue("shadowMap", 0);
_shadowMapProgram.release();
_cascadedShadowMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
Application::resourcesPath() + "shaders/cascaded_shadow_map.vert");
_cascadedShadowMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
Application::resourcesPath() + "shaders/cascaded_shadow_map.frag");
_cascadedShadowMapProgram.link();
_cascadedShadowMapProgram.bind();
_cascadedShadowMapProgram.setUniformValue("shadowMap", 0);
_shadowDistancesLocation = _cascadedShadowMapProgram.uniformLocation("shadowDistances");
_cascadedShadowMapProgram.release();
}
}
_renderer = new PrimitiveRenderer(_maxVoxels);
@ -1166,6 +1177,8 @@ glm::vec3 VoxelSystem::computeVoxelVertex(const glm::vec3& startVertex, float vo
ProgramObject VoxelSystem::_perlinModulateProgram;
ProgramObject VoxelSystem::_shadowMapProgram;
ProgramObject VoxelSystem::_cascadedShadowMapProgram;
int VoxelSystem::_shadowDistancesLocation;
void VoxelSystem::init() {
if (_initialized) {
@ -1486,14 +1499,15 @@ void VoxelSystem::render() {
void VoxelSystem::applyScaleAndBindProgram(bool texture) {
if (Menu::getInstance()->isOptionChecked(MenuOption::Shadows)) {
_shadowMapProgram.bind();
if (Menu::getInstance()->getShadowsEnabled()) {
if (Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows)) {
_cascadedShadowMapProgram.bind();
_cascadedShadowMapProgram.setUniform(_shadowDistancesLocation, Application::getInstance()->getShadowDistances());
} else {
_shadowMapProgram.bind();
}
glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getShadowDepthTextureID());
glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[0]);
glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[1]);
glTexGenfv(GL_R, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[2]);
} else if (texture) {
_perlinModulateProgram.bind();
glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getPermutationNormalTextureID());
@ -1507,11 +1521,14 @@ void VoxelSystem::removeScaleAndReleaseProgram(bool texture) {
// scale back down to 1 so heads aren't massive
glPopMatrix();
if (Menu::getInstance()->isOptionChecked(MenuOption::Shadows)) {
_shadowMapProgram.release();
if (Menu::getInstance()->getShadowsEnabled()) {
if (Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows)) {
_cascadedShadowMapProgram.release();
} else {
_shadowMapProgram.release();
}
glBindTexture(GL_TEXTURE_2D, 0);
} else if (texture) {
_perlinModulateProgram.release();
glBindTexture(GL_TEXTURE_2D, 0);

View file

@ -233,6 +233,8 @@ private:
static ProgramObject _perlinModulateProgram;
static ProgramObject _shadowMapProgram;
static ProgramObject _cascadedShadowMapProgram;
static int _shadowDistancesLocation;
int _hookID;
std::vector<glBufferIndex> _freeIndexes;

View file

@ -88,6 +88,7 @@ void AudioInjector::injectAudio() {
int currentSendPosition = 0;
int numPreAudioDataBytes = injectAudioPacket.size();
bool shouldLoop = _options.getLoop();
// loop to send off our audio in NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL byte chunks
while (currentSendPosition < soundByteArray.size() && !_shouldStop) {
@ -120,6 +121,10 @@ void AudioInjector::injectAudio() {
usleep(usecToSleep);
}
}
if (shouldLoop && currentSendPosition == soundByteArray.size()) {
currentSendPosition = 0;
}
}
}

View file

@ -15,6 +15,7 @@ AudioInjectorOptions::AudioInjectorOptions(QObject* parent) :
QObject(parent),
_position(0.0f, 0.0f, 0.0f),
_volume(1.0f),
_loop(false),
_orientation(glm::vec3(0.0f, 0.0f, 0.0f)),
_loopbackAudioInterface(NULL)
{
@ -24,6 +25,7 @@ AudioInjectorOptions::AudioInjectorOptions(QObject* parent) :
AudioInjectorOptions::AudioInjectorOptions(const AudioInjectorOptions& other) {
_position = other._position;
_volume = other._volume;
_loop = other._loop;
_orientation = other._orientation;
_loopbackAudioInterface = other._loopbackAudioInterface;
}

View file

@ -26,6 +26,7 @@ class AudioInjectorOptions : public QObject {
Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition)
Q_PROPERTY(float volume READ getVolume WRITE setVolume)
Q_PROPERTY(bool loop READ getLoop WRITE setLoop)
public:
AudioInjectorOptions(QObject* parent = 0);
AudioInjectorOptions(const AudioInjectorOptions& other);
@ -36,6 +37,9 @@ public:
float getVolume() const { return _volume; }
void setVolume(float volume) { _volume = volume; }
float getLoop() const { return _loop; }
void setLoop(float loop) { _loop = loop; }
const glm::quat& getOrientation() const { return _orientation; }
void setOrientation(const glm::quat& orientation) { _orientation = orientation; }
@ -45,6 +49,7 @@ public:
private:
glm::vec3 _position;
float _volume;
bool _loop;
glm::quat _orientation;
AbstractAudioInterface* _loopbackAudioInterface;
};

View file

@ -20,8 +20,8 @@
#include <glm/gtc/quaternion.hpp>
// degrees
const float MIN_HEAD_YAW = -110.f;
const float MAX_HEAD_YAW = 110.f;
const float MIN_HEAD_YAW = -180.f;
const float MAX_HEAD_YAW = 180.f;
const float MIN_HEAD_PITCH = -60.f;
const float MAX_HEAD_PITCH = 60.f;
const float MIN_HEAD_ROLL = -50.f;

View file

@ -1308,11 +1308,6 @@ bool OctreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3
keepSearching = true; // assume that we will continue searching after this.
// by default, we only allow intersections with leaves with content
if (!canRayIntersect()) {
return false; // we don't intersect with non-leaves, and we keep searching
}
AACube cube = getAACube();
float localDistance;
BoxFace localFace;
@ -1323,6 +1318,11 @@ bool OctreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3
return false; // we did not intersect
}
// by default, we only allow intersections with leaves with content
if (!canRayIntersect()) {
return false; // we don't intersect with non-leaves, and we keep searching
}
// we did hit this element, so calculate appropriate distances
localDistance *= TREE_SCALE;
if (localDistance < distance) {
@ -1346,6 +1346,7 @@ bool OctreeElement::findDetailedRayIntersection(const glm::vec3& origin, const g
if (intersectedObject) {
*intersectedObject = this;
}
keepSearching = false;
return true; // we did intersect
}
return false; // we did not intersect

View file

@ -52,4 +52,22 @@ PerformanceWarning::~PerformanceWarning() {
}
};
QMap<QString, PerformanceTimerRecord> PerformanceTimer::_records;
PerformanceTimer::~PerformanceTimer() {
quint64 end = usecTimestampNow();
quint64 elapsedusec = (end - _start);
PerformanceTimerRecord& namedRecord = _records[_name];
namedRecord.recordResult(elapsedusec);
}
void PerformanceTimer::dumpAllTimerRecords() {
QMapIterator<QString, PerformanceTimerRecord> i(_records);
while (i.hasNext()) {
i.next();
qDebug() << i.key() << ": average " << i.value().getAverage()
<< " [" << i.value().getMovingAverage() << "]"
<< "usecs over" << i.value().getCount() << "calls";
}
}

View file

@ -17,6 +17,7 @@
#include <stdint.h>
#include "SharedUtil.h"
#include "SimpleMovingAverage.h"
#include <cstring>
#include <string>
@ -49,5 +50,41 @@ public:
static void setSuppressShortTimings(bool suppressShortTimings) { _suppressShortTimings = suppressShortTimings; }
};
class PerformanceTimerRecord {
public:
PerformanceTimerRecord() : _runningTotal(0), _totalCalls(0) {}
void recordResult(quint64 elapsed) { _runningTotal += elapsed; _totalCalls++; _movingAverage.updateAverage(elapsed); }
quint64 getAverage() const { return (_totalCalls == 0) ? 0 : _runningTotal / _totalCalls; }
quint64 getMovingAverage() const { return (_totalCalls == 0) ? 0 : _movingAverage.getAverage(); }
quint64 getCount() const { return _totalCalls; }
private:
quint64 _runningTotal;
quint64 _totalCalls;
SimpleMovingAverage _movingAverage;
};
class PerformanceTimer {
public:
PerformanceTimer(const QString& name) :
_start(usecTimestampNow()),
_name(name) { }
quint64 elapsed() const { return (usecTimestampNow() - _start); };
~PerformanceTimer();
static const PerformanceTimerRecord& getTimerRecord(const QString& name) { return _records[name]; };
static const QMap<QString, PerformanceTimerRecord>& getAllTimerRecords() { return _records; };
static void dumpAllTimerRecords();
private:
quint64 _start;
QString _name;
static QMap<QString, PerformanceTimerRecord> _records;
};
#endif // hifi_PerfStat_h