Merge branch 'master' into 20551

This commit is contained in:
David Rowe 2015-06-09 09:36:44 -07:00
commit cb6aef16e6
168 changed files with 6355 additions and 2731 deletions

23
examples/dialTone.js Normal file
View file

@ -0,0 +1,23 @@
//
// dialTone.js
// examples
//
// Created by Stephen Birarda on 06/08/15.
// Copyright 2015 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
//
// setup the local sound we're going to use
var connectSound = SoundCache.getSound("file://" + Paths.resources + "sounds/short1.wav");
// setup the options needed for that sound
var connectSoundOptions = {
localOnly: true
}
// play the sound locally once we get the first audio packet from a mixer
Audio.receivedFirstPacket.connect(function(){
Audio.playSound(connectSound, connectSoundOptions);
});

View file

@ -0,0 +1,89 @@
// dynamicLandscape.js
// examples
//
// Created by Eric Levin on June 8
// Copyright 2015 High Fidelity, Inc.
//
// Meditative ocean landscape
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
Script.include(HIFI_PUBLIC_BUCKET + 'scripts/utilities.js')
var NUM_ROWS = 10;
var CUBE_SIZE = 1;
var cubes = [];
var cubesSettings = [];
var time = 0;
var OMEGA = 2.0 * Math.PI/8;
var RANGE = CUBE_SIZE/2;
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(CUBE_SIZE* 10, Quat.getFront(Camera.getOrientation())));
for (var x = 0, rowIndex = 0; x < NUM_ROWS * CUBE_SIZE; x += CUBE_SIZE, rowIndex++) {
for (var z = 0, columnIndex = 0; z < NUM_ROWS * CUBE_SIZE; z += CUBE_SIZE, columnIndex++) {
var baseHeight = map( columnIndex + 1, 1, NUM_ROWS, -CUBE_SIZE * 2, -CUBE_SIZE);
var relativePosition = {
x: x,
y: baseHeight,
z: z
};
var position = Vec3.sum(center, relativePosition);
cubes.push(Entities.addEntity({
type: 'Box',
position: position,
dimensions: {
x: CUBE_SIZE,
y: CUBE_SIZE,
z: CUBE_SIZE
}
}));
var phase = map( (columnIndex + 1) * (rowIndex + 1), 2, NUM_ROWS * NUM_ROWS, Math.PI * 2, Math.PI * 4);
cubesSettings.push({
baseHeight: center.y + baseHeight,
phase: phase
})
}
}
function update(deleteTime) {
time += deleteTime;
for (var i = 0; i < cubes.length; i++) {
var phase = cubesSettings[i].phase;
var props = Entities.getEntityProperties(cubes[i]);
var newHeight = Math.sin(time * OMEGA + phase) / 2.0;
var hue = map(newHeight, -.5, .5, 0.5, 0.7);
var light = map(newHeight, -.5, .5, 0.4, 0.6)
newHeight = cubesSettings[i].baseHeight + (newHeight * RANGE);
var newVelocityY = Math.cos(time * OMEGA + phase) / 2.0 * RANGE * OMEGA;
var newPosition = props.position;
var newVelocity = props.velocity;
newPosition.y = newHeight;
newVelocity = newVelocityY;
Entities.editEntity( cubes[i], {
position: newPosition,
velocity: props.velocity,
color: hslToRgb({hue: hue, sat: 0.7, light: light})
});
}
}
function cleanup() {
cubes.forEach(function(cube) {
Entities.deleteEntity(cube);
})
}
Script.update.connect(update);
Script.scriptEnding.connect(cleanup)

View file

@ -1,4 +1,3 @@
// grab.js
// examples
//
@ -11,248 +10,337 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var isGrabbing = false;
var grabbedEntity = null;
var actionID = null;
var prevMouse = {};
var deltaMouse = {
z: 0
}
var entityProps;
var moveUpDown = false;
var MOVE_TIMESCALE = 0.1;
var INV_MOVE_TIMESCALE = 1.0 / MOVE_TIMESCALE;
var MAX_SOLID_ANGLE = 0.01; // objects that appear smaller than this can't be grabbed
var CLOSE_ENOUGH = 0.001;
var FULL_STRENGTH = 0.11;
var SPRING_RATE = 1.5;
var DAMPING_RATE = 0.80;
var ZERO_VEC3 = { x: 0, y: 0, z: 0 };
var ANGULAR_DAMPING_RATE = 0.40;
var SCREEN_TO_METERS = 0.001;
var currentPosition, currentVelocity, cameraEntityDistance, currentRotation;
var velocityTowardTarget, desiredVelocity, addedVelocity, newVelocity, dPosition, camYaw, distanceToTarget, targetPosition;
var originalGravity = {x: 0, y: 0, z: 0};
var shouldRotate = false;
var dQ, theta, axisAngle, dT;
var angularVelocity = {
x: 0,
y: 0,
z: 0
// NOTE: to improve readability global variable names start with 'g'
var gIsGrabbing = false;
var gGrabbedEntity = null;
var gPrevMouse = {x: 0, y: 0};
var gEntityProperties;
var gStartPosition;
var gStartRotation;
var gCurrentPosition;
var gOriginalGravity = ZERO_VEC3;
var gPlaneNormal = ZERO_VEC3;
// gMaxGrabDistance is a function of the size of the object.
var gMaxGrabDistance;
// gGrabMode defines the degrees of freedom of the grab target positions
// relative to gGrabStartPosition options include:
// xzPlane (default)
// verticalCylinder (SHIFT)
// rotate (CONTROL)
// Modes to eventually support?:
// xyPlane
// yzPlane
// polar
// elevationAzimuth
var gGrabMode = "xzplane";
// gGrabOffset allows the user to grab an object off-center. It points from ray's intersection
// with the move-plane to object center (at the moment the grab is initiated). Future target positions
// are relative to the ray's intersection by the same offset.
var gGrabOffset = { x: 0, y: 0, z: 0 };
var gTargetPosition;
var gTargetRotation;
var gLiftKey = false; // SHIFT
var gRotateKey = false; // CONTROL
var gPreviousMouse = { x: 0, y: 0 };
var gMouseCursorLocation = { x: 0, y: 0 };
var gMouseAtRotateStart = { x: 0, y: 0 };
var gBeaconHeight = 0.10;
var gAngularVelocity = ZERO_VEC3;
// TODO: play sounds again when we aren't leaking AudioInjector threads
// var grabSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/CloseClamp.wav");
// var releaseSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/ReleaseClamp.wav");
// var VOLUME = 0.0;
var gBeaconHeight = 0.10;
var BEACON_COLOR = {
red: 200,
green: 200,
blue: 200
};
var BEACON_WIDTH = 2;
var DROP_DISTANCE = 5.0;
var DROP_COLOR = {
red: 200,
green: 200,
blue: 200
};
var DROP_WIDTH = 2;
var dropLine = Overlays.addOverlay("line3d", {
color: DROP_COLOR,
alpha: 1,
visible: false,
lineWidth: DROP_WIDTH
var gBeacon = Overlays.addOverlay("line3d", {
color: BEACON_COLOR,
alpha: 1,
visible: false,
lineWidth: BEACON_WIDTH
});
function vectorIsZero(v) {
return v.x == 0 && v.y == 0 && v.z == 0;
function updateDropLine(position) {
Overlays.editOverlay(gBeacon, {
visible: true,
start: {
x: position.x,
y: position.y + gBeaconHeight,
z: position.z
},
end: {
x: position.x,
y: position.y - gBeaconHeight,
z: position.z
}
});
}
function nearLinePoint(targetPosition) {
// var handPosition = Vec3.sum(MyAvatar.position, {x:0, y:0.2, z:0});
var handPosition = MyAvatar.getRightPalmPosition();
var along = Vec3.subtract(targetPosition, handPosition);
along = Vec3.normalize(along);
along = Vec3.multiply(along, 0.4);
return Vec3.sum(handPosition, along);
function mouseIntersectionWithPlane(pointOnPlane, planeNormal, event) {
var cameraPosition = Camera.getPosition();
var localPointOnPlane = Vec3.subtract(pointOnPlane, cameraPosition);
var distanceFromPlane = Vec3.dot(localPointOnPlane, planeNormal);
var MIN_DISTANCE_FROM_PLANE = 0.001;
if (Math.abs(distanceFromPlane) < MIN_DISTANCE_FROM_PLANE) {
// camera is touching the plane
return pointOnPlane;
}
var pickRay = Camera.computePickRay(event.x, event.y);
var dirDotNorm = Vec3.dot(pickRay.direction, planeNormal);
var MIN_RAY_PLANE_DOT = 0.00001;
var localIntersection;
var useMaxForwardGrab = false;
if (Math.abs(dirDotNorm) > MIN_RAY_PLANE_DOT) {
var distanceToIntersection = distanceFromPlane / dirDotNorm;
if (distanceToIntersection > 0 && distanceToIntersection < gMaxGrabDistance) {
// ray points into the plane
localIntersection = Vec3.multiply(pickRay.direction, distanceFromPlane / dirDotNorm);
} else {
// ray intersects BEHIND the camera or else very far away
// so we clamp the grab point to be the maximum forward position
useMaxForwardGrab = true;
}
} else {
// ray points perpendicular to grab plane
// so we map the grab point to the maximum forward position
useMaxForwardGrab = true;
}
if (useMaxForwardGrab) {
// we re-route the intersection to be in front at max distance.
var rayDirection = Vec3.subtract(pickRay.direction, Vec3.multiply(planeNormal, dirDotNorm));
rayDirection = Vec3.normalize(rayDirection);
localIntersection = Vec3.multiply(rayDirection, gMaxGrabDistance);
localIntersection = Vec3.sum(localIntersection, Vec3.multiply(planeNormal, distanceFromPlane));
}
var worldIntersection = Vec3.sum(cameraPosition, localIntersection);
return worldIntersection;
}
function computeNewGrabPlane() {
var maybeResetMousePosition = false;
if (gGrabMode !== "rotate") {
gMouseAtRotateStart = gMouseCursorLocation;
} else {
maybeResetMousePosition = true;
}
gGrabMode = "xzPlane";
gPointOnPlane = gCurrentPosition;
gPlaneNormal = { x: 0, y: 1, z: 0 };
if (gLiftKey) {
if (!gRotateKey) {
gGrabMode = "verticalCylinder";
// a new planeNormal will be computed each move
}
} else if (gRotateKey) {
gGrabMode = "rotate";
}
gPointOnPlane = Vec3.subtract(gCurrentPosition, gGrabOffset);
var xzOffset = Vec3.subtract(gPointOnPlane, Camera.getPosition());
xzOffset.y = 0;
gXzDistanceToGrab = Vec3.length(xzOffset);
if (gGrabMode !== "rotate" && maybeResetMousePosition) {
// we reset the mouse position whenever we stop rotating
Window.setCursorPosition(gMouseAtRotateStart.x, gMouseAtRotateStart.y);
}
}
function mousePressEvent(event) {
if (!event.isLeftButton) {
return;
}
var pickRay = Camera.computePickRay(event.x, event.y);
var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking
if (intersection.intersects && intersection.properties.collisionsWillMove) {
grabbedEntity = intersection.entityID;
var props = Entities.getEntityProperties(grabbedEntity)
isGrabbing = true;
originalGravity = props.gravity;
targetPosition = props.position;
currentPosition = props.position;
currentVelocity = props.velocity;
updateDropLine(targetPosition);
Entities.editEntity(grabbedEntity, {
gravity: {x: 0, y: 0, z: 0}
});
}
}
function updateDropLine(position) {
Overlays.editOverlay(dropLine, {
visible: true,
start: {
x: position.x,
y: position.y + DROP_DISTANCE,
z: position.z
},
end: {
x: position.x,
y: position.y - DROP_DISTANCE,
z: position.z
if (!event.isLeftButton) {
return;
}
})
}
gPreviousMouse = {x: event.x, y: event.y };
var pickRay = Camera.computePickRay(event.x, event.y);
var pickResults = Entities.findRayIntersection(pickRay, true); // accurate picking
if (!pickResults.intersects) {
// didn't click on anything
return;
}
if (!pickResults.properties.collisionsWillMove) {
// only grab dynamic objects
return;
}
var clickedEntity = pickResults.entityID;
var entityProperties = Entities.getEntityProperties(clickedEntity)
var objectPosition = entityProperties.position;
var cameraPosition = Camera.getPosition();
gBeaconHeight = Vec3.length(entityProperties.dimensions);
gMaxGrabDistance = gBeaconHeight / MAX_SOLID_ANGLE;
if (Vec3.distance(objectPosition, cameraPosition) > gMaxGrabDistance) {
// don't allow grabs of things far away
return;
}
Entities.editEntity(clickedEntity, { gravity: ZERO_VEC3 });
gIsGrabbing = true;
gGrabbedEntity = clickedEntity;
gCurrentPosition = entityProperties.position;
gOriginalGravity = entityProperties.gravity;
gTargetPosition = objectPosition;
// compute the grab point
var nearestPoint = Vec3.subtract(objectPosition, cameraPosition);
var distanceToGrab = Vec3.dot(nearestPoint, pickRay.direction);
nearestPoint = Vec3.multiply(distanceToGrab, pickRay.direction);
gPointOnPlane = Vec3.sum(cameraPosition, nearestPoint);
// compute the grab offset
gGrabOffset = Vec3.subtract(objectPosition, gPointOnPlane);
computeNewGrabPlane();
updateDropLine(objectPosition);
// TODO: play sounds again when we aren't leaking AudioInjector threads
//Audio.playSound(grabSound, { position: entityProperties.position, volume: VOLUME });
}
function mouseReleaseEvent() {
if (isGrabbing) {
// Controller.mouseMoveEvent.disconnect(mouseMoveEvent);
isGrabbing = false;
Entities.deleteAction(grabbedEntity, actionID);
actionID = null;
if (gIsGrabbing) {
if (Vec3.length(gOriginalGravity) != 0) {
Entities.editEntity(gGrabbedEntity, { gravity: gOriginalGravity });
}
// only restore the original gravity if it's not zero. This is to avoid...
// 1. interface A grabs an entity and locally saves off its gravity
// 2. interface A sets the entity's gravity to zero
// 3. interface B grabs the entity and saves off its gravity (which is zero)
// 4. interface A releases the entity and puts the original gravity back
// 5. interface B releases the entity and puts the original gravity back (to zero)
if (!vectorIsZero(originalGravity)) {
Entities.editEntity(grabbedEntity, {
gravity: originalGravity
});
gIsGrabbing = false
Overlays.editOverlay(gBeacon, { visible: false });
// TODO: play sounds again when we aren't leaking AudioInjector threads
//Audio.playSound(releaseSound, { position: entityProperties.position, volume: VOLUME });
}
Overlays.editOverlay(dropLine, {
visible: false
});
targetPosition = null;
}
}
function mouseMoveEvent(event) {
if (isGrabbing) {
if (!gIsGrabbing) {
return;
}
// see if something added/restored gravity
var props = Entities.getEntityProperties(grabbedEntity);
if (!vectorIsZero(props.gravity)) {
originalGravity = props.gravity;
var entityProperties = Entities.getEntityProperties(gGrabbedEntity);
if (Vec3.length(entityProperties.gravity) != 0) {
gOriginalGravity = entityProperties.gravity;
}
deltaMouse.x = event.x - prevMouse.x;
if (!moveUpDown) {
deltaMouse.z = event.y - prevMouse.y;
deltaMouse.y = 0;
if (gGrabMode === "rotate") {
var deltaMouse = { x: 0, y: 0 };
var dx = event.x - gPreviousMouse.x;
var dy = event.y - gPreviousMouse.y;
var orientation = Camera.getOrientation();
var dragOffset = Vec3.multiply(dx, Quat.getRight(orientation));
dragOffset = Vec3.sum(dragOffset, Vec3.multiply(-dy, Quat.getUp(orientation)));
var axis = Vec3.cross(dragOffset, Quat.getFront(orientation));
var axis = Vec3.normalize(axis);
var ROTATE_STRENGTH = 8.0; // magic number tuned by hand
gAngularVelocity = Vec3.multiply(ROTATE_STRENGTH, axis);
} else {
deltaMouse.y = (event.y - prevMouse.y) * -1;
deltaMouse.z = 0;
var newTargetPosition;
if (gGrabMode === "verticalCylinder") {
// for this mode we recompute the plane based on current Camera
var planeNormal = Quat.getFront(Camera.getOrientation());
planeNormal.y = 0;
planeNormal = Vec3.normalize(planeNormal);
var pointOnCylinder = Vec3.multiply(planeNormal, gXzDistanceToGrab);
pointOnCylinder = Vec3.sum(Camera.getPosition(), pointOnCylinder);
newTargetPosition = mouseIntersectionWithPlane(pointOnCylinder, planeNormal, event);
} else {
var cameraPosition = Camera.getPosition();
newTargetPosition = mouseIntersectionWithPlane(gPointOnPlane, gPlaneNormal, event);
var relativePosition = Vec3.subtract(newTargetPosition, cameraPosition);
var distance = Vec3.length(relativePosition);
if (distance > gMaxGrabDistance) {
// clamp distance
relativePosition = Vec3.multiply(relativePosition, gMaxGrabDistance / distance);
newTargetPosition = Vec3.sum(relativePosition, cameraPosition);
}
}
gTargetPosition = Vec3.sum(newTargetPosition, gGrabOffset);
}
// Update the target position by the amount the mouse moved
camYaw = Quat.safeEulerAngles(Camera.getOrientation()).y;
dPosition = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, camYaw, 0), deltaMouse);
if (!shouldRotate) {
// Adjust target position for the object by the mouse move
cameraEntityDistance = Vec3.distance(Camera.getPosition(), currentPosition);
// Scale distance we want to move by the distance from the camera to the grabbed object
// TODO: Correct SCREEN_TO_METERS to be correct for the actual FOV, resolution
targetPosition = Vec3.sum(targetPosition, Vec3.multiply(dPosition, cameraEntityDistance * SCREEN_TO_METERS));
} else if (shouldRotate) {
var transformedDeltaMouse = {
x: deltaMouse.z,
y: deltaMouse.x,
z: deltaMouse.y
};
transformedDeltaMouse = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, camYaw, 0), transformedDeltaMouse);
dQ = Quat.fromVec3Degrees(transformedDeltaMouse);
theta = 2 * Math.acos(dQ.w);
axisAngle = Quat.axis(dQ);
angularVelocity = Vec3.multiply((theta / dT), axisAngle);
}
}
prevMouse.x = event.x;
prevMouse.y = event.y;
gPreviousMouse = { x: event.x, y: event.y };
gMouseCursorLocation = { x: Window.getCursorPositionX(), y: Window.getCursorPositionY() };
}
function keyReleaseEvent(event) {
if (event.text === "SHIFT") {
moveUpDown = false;
}
if (event.text === "SPACE") {
shouldRotate = false;
}
if (event.text === "SHIFT") {
gLiftKey = false;
}
if (event.text === "CONTROL") {
gRotateKey = false;
}
computeNewGrabPlane();
}
function keyPressEvent(event) {
if (event.text === "SHIFT") {
moveUpDown = true;
}
if (event.text === "SPACE") {
shouldRotate = true;
}
if (event.text === "SHIFT") {
gLiftKey = true;
}
if (event.text === "CONTROL") {
gRotateKey = true;
}
computeNewGrabPlane();
}
function update(deltaTime) {
dT = deltaTime;
if (isGrabbing) {
entityProps = Entities.getEntityProperties(grabbedEntity);
currentPosition = entityProps.position;
currentVelocity = entityProps.velocity;
currentRotation = entityProps.rotation;
var dPosition = Vec3.subtract(targetPosition, currentPosition);
distanceToTarget = Vec3.length(dPosition);
if (distanceToTarget > CLOSE_ENOUGH) {
// compute current velocity in the direction we want to move
velocityTowardTarget = Vec3.dot(currentVelocity, Vec3.normalize(dPosition));
velocityTowardTarget = Vec3.multiply(Vec3.normalize(dPosition), velocityTowardTarget);
// compute the speed we would like to be going toward the target position
desiredVelocity = Vec3.multiply(dPosition, (1.0 / deltaTime) * SPRING_RATE);
// compute how much we want to add to the existing velocity
addedVelocity = Vec3.subtract(desiredVelocity, velocityTowardTarget);
// If target is too far, roll off the force as inverse square of distance
if (distanceToTarget / cameraEntityDistance > FULL_STRENGTH) {
addedVelocity = Vec3.multiply(addedVelocity, Math.pow(FULL_STRENGTH / distanceToTarget, 2.0));
}
newVelocity = Vec3.sum(currentVelocity, addedVelocity);
// Add Damping
newVelocity = Vec3.subtract(newVelocity, Vec3.multiply(newVelocity, DAMPING_RATE));
// Update entity
} else {
newVelocity = {x: 0, y: 0, z: 0};
}
if (shouldRotate) {
angularVelocity = Vec3.subtract(angularVelocity, Vec3.multiply(angularVelocity, ANGULAR_DAMPING_RATE));
Entities.editEntity(grabbedEntity, {
rotation: currentRotation,
angularVelocity: angularVelocity
});
} else {
angularVelocity = entityProps.angularVelocity;
if (!gIsGrabbing) {
return;
}
var newSpeed = Vec3.length(newVelocity);
if (!actionID) {
actionID = Entities.addAction("pull-to-point", grabbedEntity, {target: targetPosition, speed: newSpeed});
} else {
Entities.updateAction(grabbedEntity, actionID, {target: targetPosition, speed: newSpeed});
}
var entityProperties = Entities.getEntityProperties(gGrabbedEntity);
gCurrentPosition = entityProperties.position;
if (gGrabMode === "rotate") {
gAngularVelocity = Vec3.subtract(gAngularVelocity, Vec3.multiply(gAngularVelocity, ANGULAR_DAMPING_RATE));
Entities.editEntity(gGrabbedEntity, { angularVelocity: gAngularVelocity, });
}
updateDropLine(targetPosition);
}
// always push toward linear grab position, even when rotating
var newVelocity = ZERO_VEC3;
var dPosition = Vec3.subtract(gTargetPosition, gCurrentPosition);
var delta = Vec3.length(dPosition);
if (delta > CLOSE_ENOUGH) {
var MAX_POSITION_DELTA = 4.0;
if (delta > MAX_POSITION_DELTA) {
dPosition = Vec3.multiply(dPosition, MAX_POSITION_DELTA / delta);
}
// desired speed is proportional to displacement by the inverse of timescale
// (for critically damped motion)
newVelocity = Vec3.multiply(dPosition, INV_MOVE_TIMESCALE);
}
Entities.editEntity(gGrabbedEntity, { velocity: newVelocity, });
updateDropLine(gTargetPosition);
}
Controller.mouseMoveEvent.connect(mouseMoveEvent);
Controller.mousePressEvent.connect(mousePressEvent);
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
Controller.mouseMoveEvent.connect(mouseMoveEvent);
Controller.keyPressEvent.connect(keyPressEvent);
Controller.keyReleaseEvent.connect(keyReleaseEvent);
Script.update.connect(update);

View file

@ -12,7 +12,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include('lineRider.js')
var MAX_POINTS_PER_LINE = 30;
var MAX_POINTS_PER_LINE = 80;
var colorPalette = [{

56
examples/utilities.js Normal file
View file

@ -0,0 +1,56 @@
// utilities.js
// examples
//
// Created by Eric Levin on June 8
// Copyright 2015 High Fidelity, Inc.
//
// Common utilities
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
function hslToRgb(hslColor) {
var h = hslColor.hue;
var s = hslColor.sat;
var l = hslColor.light;
var r, g, b;
if (s == 0) {
r = g = b = l; // achromatic
} else {
var hue2rgb = function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
return {
red: Math.round(r * 255),
green: Math.round(g * 255),
blue: Math.round(b * 255)
};
}
function map(value, min1, max1, min2, max2) {
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
}
function randFloat(low, high) {
return low + Math.random() * (high - low);
}
function randInt(low, high) {
return Math.floor(randFloat(low, high));
}

View file

@ -134,7 +134,7 @@ Slider = function(x,y,width,thumbSize) {
}
// The Checkbox class
Checkbox = function(x,y,width,thumbSize) {
Checkbox = function(x,y,thumbSize) {
this.thumb = Overlays.addOverlay("text", {
backgroundColor: { red: 255, green: 255, blue: 255 },
@ -150,7 +150,7 @@ Checkbox = function(x,y,width,thumbSize) {
backgroundColor: { red: 125, green: 125, blue: 255 },
x: x,
y: y,
width: width,
width: thumbSize * 2,
height: thumbSize,
alpha: 1.0,
backgroundAlpha: 0.5,
@ -161,7 +161,7 @@ Checkbox = function(x,y,width,thumbSize) {
this.thumbHalfSize = 0.5 * thumbSize;
this.minThumbX = x + this.thumbHalfSize;
this.maxThumbX = x + width - this.thumbHalfSize;
this.maxThumbX = x + thumbSize * 2 - this.thumbHalfSize;
this.thumbX = this.minThumbX;
this.minValue = 0.0;
@ -553,7 +553,7 @@ Panel = function(x, y) {
var item = new PanelItem(name, setValue, getValue, displayValue, this.x, this.nextY, textWidth, valueWidth, rawHeight);
var checkbox = new Checkbox(this.widgetX, this.nextY, widgetWidth, rawHeight);
var checkbox = new Checkbox(this.widgetX, this.nextY, rawHeight);
item.widget = checkbox;
item.widget.onValueChanged = function(value) { item.setterFromWidget(value); };

View file

@ -0,0 +1,104 @@
//
// SunLightExample.js
// examples
// Sam Gateau
// Copyright 2015 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
//
Script.include("cookies.js");
var panel = new Panel(10, 400);
panel.newCheckbox("Enable Cull Opaque",
function(value) { Scene.setEngineCullOpaque((value != 0)); },
function() { return Scene.doEngineCullOpaque(); },
function(value) { return (value); }
);
panel.newCheckbox("Enable Sort Opaque",
function(value) { Scene.setEngineSortOpaque((value != 0)); },
function() { return Scene.doEngineSortOpaque(); },
function(value) { return (value); }
);
panel.newCheckbox("Enable Render Opaque",
function(value) { Scene.setEngineRenderOpaque((value != 0)); },
function() { return Scene.doEngineRenderOpaque(); },
function(value) { return (value); }
);
panel.newSlider("Num Feed Opaques", 0, 1000,
function(value) { },
function() { return Scene.getEngineNumFeedOpaqueItems(); },
function(value) { return (value); }
);
panel.newSlider("Num Drawn Opaques", 0, 1000,
function(value) { },
function() { return Scene.getEngineNumDrawnOpaqueItems(); },
function(value) { return (value); }
);
panel.newSlider("Max Drawn Opaques", -1, 1000,
function(value) { Scene.setEngineMaxDrawnOpaqueItems(value); },
function() { return Scene.getEngineMaxDrawnOpaqueItems(); },
function(value) { return (value); }
);
panel.newCheckbox("Enable Cull Transparent",
function(value) { Scene.setEngineCullTransparent((value != 0)); },
function() { return Scene.doEngineCullTransparent(); },
function(value) { return (value); }
);
panel.newCheckbox("Enable Sort Transparent",
function(value) { Scene.setEngineSortTransparent((value != 0)); },
function() { return Scene.doEngineSortTransparent(); },
function(value) { return (value); }
);
panel.newCheckbox("Enable Render Transparent",
function(value) { Scene.setEngineRenderTransparent((value != 0)); },
function() { return Scene.doEngineRenderTransparent(); },
function(value) { return (value); }
);
panel.newSlider("Num Feed Transparents", 0, 100,
function(value) { },
function() { return Scene.getEngineNumFeedTransparentItems(); },
function(value) { return (value); }
);
panel.newSlider("Num Drawn Transparents", 0, 100,
function(value) { },
function() { return Scene.getEngineNumDrawnTransparentItems(); },
function(value) { return (value); }
);
panel.newSlider("Max Drawn Transparents", -1, 100,
function(value) { Scene.setEngineMaxDrawnTransparentItems(value); },
function() { return Scene.getEngineMaxDrawnTransparentItems(); },
function(value) { return (value); }
);
var tickTackPeriod = 500;
function updateCounters() {
panel.set("Num Feed Opaques", panel.get("Num Feed Opaques"));
panel.set("Num Drawn Opaques", panel.get("Num Drawn Opaques"));
panel.set("Num Feed Transparents", panel.get("Num Feed Transparents"));
panel.set("Num Drawn Transparents", panel.get("Num Drawn Transparents"));
}
Script.setInterval(updateCounters, tickTackPeriod);
Controller.mouseMoveEvent.connect(function panelMouseMoveEvent(event) { return panel.mouseMoveEvent(event); });
Controller.mousePressEvent.connect( function panelMousePressEvent(event) { return panel.mousePressEvent(event); });
Controller.mouseReleaseEvent.connect(function(event) { return panel.mouseReleaseEvent(event); });
function scriptEnding() {
panel.destroy();
}
Script.scriptEnding.connect(scriptEnding);

View file

@ -138,7 +138,7 @@ target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${BULLET_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES})
# link required hifi libraries
link_hifi_libraries(shared octree environment gpu model fbx networking entities avatars
link_hifi_libraries(shared octree environment gpu model render fbx networking entities avatars
audio audio-client animation script-engine physics
render-utils entities-renderer ui)

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -77,6 +77,7 @@
#include "octree/OctreePacketProcessor.h"
#include "UndoStackScriptingInterface.h"
#include "render/Engine.h"
class QGLWidget;
class QKeyEvent;
@ -209,7 +210,6 @@ public:
ViewFrustum* getShadowViewFrustum() { return &_shadowViewFrustum; }
const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; }
EntityTreeRenderer* getEntities() { return &_entities; }
Environment* getEnvironment() { return &_environment; }
QUndoStack* getUndoStack() { return &_undoStack; }
MainWindow* getWindow() { return _window; }
OctreeQuery& getOctreeQuery() { return _octreeQuery; }
@ -268,9 +268,9 @@ public:
virtual void setupWorldLight();
virtual bool shouldRenderMesh(float largestDimension, float distanceToCamera);
QImage renderAvatarBillboard();
QImage renderAvatarBillboard(RenderArgs* renderArgs);
void displaySide(Camera& whichCamera, bool selfAvatarOnly = false, bool billboard = false, RenderArgs::RenderSide renderSide = RenderArgs::MONO);
void displaySide(RenderArgs* renderArgs, Camera& whichCamera, bool selfAvatarOnly = false, bool billboard = false);
/// Stores the current modelview matrix as the untranslated view matrix to use for transforms and the supplied vector as
/// the view matrix translation.
@ -347,6 +347,11 @@ public:
void setMaxOctreePacketsPerSecond(int maxOctreePPS);
int getMaxOctreePacketsPerSecond();
render::ScenePointer getMain3DScene() { return _main3DScene; }
render::EnginePointer getRenderEngine() { return _renderEngine; }
render::ScenePointer getMain3DScene() const { return _main3DScene; }
signals:
@ -499,8 +504,8 @@ private:
glm::vec3 getSunDirection();
void updateShadowMap();
void renderRearViewMirror(const QRect& region, bool billboard = false);
void updateShadowMap(RenderArgs* renderArgs);
void renderRearViewMirror(RenderArgs* renderArgs, const QRect& region, bool billboard = false);
void setMenuShortcutsEnabled(bool enabled);
static void attachNewHeadToNode(Node *newNode);
@ -529,7 +534,6 @@ private:
QElapsedTimer _timerStart;
QElapsedTimer _lastTimeUpdated;
bool _justStarted;
Stars _stars;
ShapeManager _shapeManager;
PhysicalEntitySimulation _entitySimulation;
@ -631,9 +635,6 @@ private:
TouchEvent _lastTouchEvent;
Overlays _overlays;
ApplicationOverlay _applicationOverlay;
RunningScriptsWidget* _runningScriptsWidget;
QHash<QString, ScriptEngine*> _scriptEnginesHash;
bool _runningScriptsWidgetWasVisible;
@ -670,6 +671,12 @@ private:
int _maxOctreePPS = DEFAULT_MAX_OCTREE_PPS;
quint64 _lastFaceTrackerUpdate;
render::ScenePointer _main3DScene{ new render::Scene() };
render::EnginePointer _renderEngine{ new render::Engine() };
Overlays _overlays;
ApplicationOverlay _applicationOverlay;
};
#endif // hifi_Application_h

View file

@ -68,7 +68,7 @@ void Environment::resetToDefault() {
_data[HifiSockAddr()][0];
}
void Environment::renderAtmospheres(Camera& camera) {
void Environment::renderAtmospheres(ViewFrustum& camera) {
// get the lock for the duration of the call
QMutexLocker locker(&_mutex);
@ -228,7 +228,7 @@ ProgramObject* Environment::createSkyProgram(const char* from, int* locations) {
return program;
}
void Environment::renderAtmosphere(Camera& camera, const EnvironmentData& data) {
void Environment::renderAtmosphere(ViewFrustum& camera, const EnvironmentData& data) {
glm::vec3 center = data.getAtmosphereCenter();
glPushMatrix();

View file

@ -19,7 +19,7 @@
#include "EnvironmentData.h"
class Camera;
class ViewFrustum;
class ProgramObject;
class Environment {
@ -29,7 +29,7 @@ public:
void init();
void resetToDefault();
void renderAtmospheres(Camera& camera);
void renderAtmospheres(ViewFrustum& camera);
void override(const EnvironmentData& overrideData) { _overrideData = overrideData; _environmentIsOverridden = true; }
void endOverride() { _environmentIsOverridden = false; }
@ -46,7 +46,7 @@ private:
ProgramObject* createSkyProgram(const char* from, int* locations);
void renderAtmosphere(Camera& camera, const EnvironmentData& data);
void renderAtmosphere(ViewFrustum& camera, const EnvironmentData& data);
bool _initialized;
ProgramObject* _skyFromAtmosphereProgram;

View file

@ -217,6 +217,47 @@ QString LODManager::getLODFeedbackText() {
return result;
}
bool LODManager::shouldRender(const RenderArgs* args, const AABox& bounds) {
const float maxScale = (float)TREE_SCALE;
const float octreeToMeshRatio = 4.0f; // must be this many times closer to a mesh than a voxel to see it.
float octreeSizeScale = args->_sizeScale;
int boundaryLevelAdjust = args->_boundaryLevelAdjust;
float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale) / octreeToMeshRatio;
float distanceToCamera = glm::length(bounds.calcCenter() - args->_viewFrustum->getPosition());
float largestDimension = bounds.getLargestDimension();
static bool shouldRenderTableNeedsBuilding = true;
static QMap<float, float> shouldRenderTable;
if (shouldRenderTableNeedsBuilding) {
float SMALLEST_SCALE_IN_TABLE = 0.001f; // 1mm is plenty small
float scale = maxScale;
float factor = 1.0f;
while (scale > SMALLEST_SCALE_IN_TABLE) {
scale /= 2.0f;
factor /= 2.0f;
shouldRenderTable[scale] = factor;
}
shouldRenderTableNeedsBuilding = false;
}
float closestScale = maxScale;
float visibleDistanceAtClosestScale = visibleDistanceAtMaxScale;
QMap<float, float>::const_iterator lowerBound = shouldRenderTable.lowerBound(largestDimension);
if (lowerBound != shouldRenderTable.constEnd()) {
closestScale = lowerBound.key();
visibleDistanceAtClosestScale = visibleDistanceAtMaxScale * lowerBound.value();
}
if (closestScale < largestDimension) {
visibleDistanceAtClosestScale *= 2.0f;
}
return distanceToCamera <= visibleDistanceAtClosestScale;
};
// TODO: This is essentially the same logic used to render octree cells, but since models are more detailed then octree cells
// I've added a voxelToModelRatio that adjusts how much closer to a model you have to be to see it.
bool LODManager::shouldRenderMesh(float largestDimension, float distanceToCamera) {

View file

@ -49,6 +49,8 @@ const float ADJUST_LOD_MAX_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE;
// do. But both are still culled using the same angular size logic.
const float AVATAR_TO_ENTITY_RATIO = 2.0f;
class RenderArgs;
class AABox;
class LODManager : public QObject, public Dependency {
Q_OBJECT
@ -79,6 +81,7 @@ public:
Q_INVOKABLE float getLODDecreaseFPS();
Q_INVOKABLE float getLODIncreaseFPS();
static bool shouldRender(const RenderArgs* args, const AABox& bounds);
bool shouldRenderMesh(float largestDimension, float distanceToCamera);
void autoAdjustLOD(float currentFPS);

View file

@ -33,45 +33,44 @@
using namespace std;
void renderWorldBox() {
void renderWorldBox(gpu::Batch& batch) {
auto geometryCache = DependencyManager::get<GeometryCache>();
// Show edge of world
glm::vec3 red(1.0f, 0.0f, 0.0f);
glm::vec3 green(0.0f, 1.0f, 0.0f);
glm::vec3 blue(0.0f, 0.0f, 1.0f);
glm::vec3 grey(0.5f, 0.5f, 0.5f);
glDisable(GL_LIGHTING);
glLineWidth(1.0);
geometryCache->renderLine(glm::vec3(0, 0, 0), glm::vec3(TREE_SCALE, 0, 0), red);
geometryCache->renderLine(glm::vec3(0, 0, 0), glm::vec3(0, TREE_SCALE, 0), green);
geometryCache->renderLine(glm::vec3(0, 0, 0), glm::vec3(0, 0, TREE_SCALE), blue);
geometryCache->renderLine(glm::vec3(0, 0, TREE_SCALE), glm::vec3(TREE_SCALE, 0, TREE_SCALE), grey);
geometryCache->renderLine(glm::vec3(TREE_SCALE, 0, TREE_SCALE), glm::vec3(TREE_SCALE, 0, 0), grey);
static const glm::vec3 red(1.0f, 0.0f, 0.0f);
static const glm::vec3 green(0.0f, 1.0f, 0.0f);
static const glm::vec3 blue(0.0f, 0.0f, 1.0f);
static const glm::vec3 grey(0.5f, 0.5f, 0.5f);
auto transform = Transform{};
batch.setModelTransform(transform);
geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(TREE_SCALE, 0.0f, 0.0f), red);
geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, TREE_SCALE, 0.0f), green);
geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, TREE_SCALE), blue);
geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, TREE_SCALE), glm::vec3(TREE_SCALE, 0.0f, TREE_SCALE), grey);
geometryCache->renderLine(batch, glm::vec3(TREE_SCALE, 0.0f, TREE_SCALE), glm::vec3(TREE_SCALE, 0.0f, 0.0f), grey);
// Draw meter markers along the 3 axis to help with measuring things
const float MARKER_DISTANCE = 1.0f;
const float MARKER_RADIUS = 0.05f;
glEnable(GL_LIGHTING);
glPushMatrix();
glTranslatef(MARKER_DISTANCE, 0, 0);
geometryCache->renderSphere(MARKER_RADIUS, 10, 10, red);
glPopMatrix();
glPushMatrix();
glTranslatef(0, MARKER_DISTANCE, 0);
geometryCache->renderSphere(MARKER_RADIUS, 10, 10, green);
glPopMatrix();
glPushMatrix();
glTranslatef(0, 0, MARKER_DISTANCE);
geometryCache->renderSphere(MARKER_RADIUS, 10, 10, blue);
glPopMatrix();
glPushMatrix();
glTranslatef(MARKER_DISTANCE, 0, MARKER_DISTANCE);
geometryCache->renderSphere(MARKER_RADIUS, 10, 10, grey);
glPopMatrix();
geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, red);
transform.setTranslation(glm::vec3(MARKER_DISTANCE, 0.0f, 0.0f));
batch.setModelTransform(transform);
geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, red);
transform.setTranslation(glm::vec3(0.0f, MARKER_DISTANCE, 0.0f));
batch.setModelTransform(transform);
geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, green);
transform.setTranslation(glm::vec3(0.0f, 0.0f, MARKER_DISTANCE));
batch.setModelTransform(transform);
geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, blue);
transform.setTranslation(glm::vec3(MARKER_DISTANCE, 0.0f, MARKER_DISTANCE));
batch.setModelTransform(transform);
geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, grey);
}
// Return a random vector of average length 1

View file

@ -16,10 +16,12 @@
#include <glm/gtc/quaternion.hpp>
#include <QSettings>
#include <gpu/Batch.h>
float randFloat();
const glm::vec3 randVector();
void renderWorldBox();
void renderWorldBox(gpu::Batch& batch);
int widthText(float scale, int mono, char const* string);
void drawText(int x, int y, float scale, float radians, int mono,

View file

@ -40,13 +40,13 @@ void AudioToolBox::render(int x, int y, int padding, bool boxed) {
glEnable(GL_TEXTURE_2D);
if (!_micTexture) {
_micTexture = DependencyManager::get<TextureCache>()->getImageTexture(PathUtils::resourcesPath() + "images/mic.svg");
_micTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/mic.svg");
}
if (!_muteTexture) {
_muteTexture = DependencyManager::get<TextureCache>()->getImageTexture(PathUtils::resourcesPath() + "images/mic-mute.svg");
_muteTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/mic-mute.svg");
}
if (_boxTexture) {
_boxTexture = DependencyManager::get<TextureCache>()->getImageTexture(PathUtils::resourcesPath() + "images/audio-box.svg");
_boxTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/audio-box.svg");
}
auto audioIO = DependencyManager::get<AudioClient>();

View file

@ -32,7 +32,7 @@
#include <PathUtils.h>
#include <PerfStat.h>
#include <SharedUtil.h>
#include <TextRenderer.h>
#include <TextRenderer3D.h>
#include <TextureCache.h>
#include "Application.h"
@ -60,6 +60,24 @@ const float DISPLAYNAME_FADE_FACTOR = pow(0.01f, 1.0f / DISPLAYNAME_FADE_TIME);
const float DISPLAYNAME_ALPHA = 0.95f;
const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f;
namespace render {
template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) {
return ItemKey::Builder::opaqueShape();
}
template <> const Item::Bound payloadGetBound(const AvatarSharedPointer& avatar) {
return static_cast<Avatar*>(avatar.get())->getBounds();
}
template <> void payloadRender(const AvatarSharedPointer& avatar, RenderArgs* args) {
Avatar* avatarPtr = static_cast<Avatar*>(avatar.get());
bool renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::RenderLookAtVectors);
avatarPtr->setDisplayingLookatVectors(renderLookAtVectors);
if (avatarPtr->isInitialized() && args) {
avatarPtr->render(args, Application::getInstance()->getCamera()->getPosition());
}
}
}
Avatar::Avatar() :
AvatarData(),
_skeletonModel(this),
@ -116,6 +134,10 @@ glm::quat Avatar::getWorldAlignedOrientation () const {
return computeRotationFromBodyToWorldUp() * getOrientation();
}
AABox Avatar::getBounds() const {
return AABox();
}
float Avatar::getLODDistance() const {
return DependencyManager::get<LODManager>()->getAvatarLODDistanceMultiplier() *
glm::distance(qApp->getCamera()->getPosition(), _position) / _scale;
@ -254,10 +276,10 @@ enum TextRendererType {
DISPLAYNAME
};
static TextRenderer* textRenderer(TextRendererType type) {
static TextRenderer* chatRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, 24, -1,
false, TextRenderer::SHADOW_EFFECT);
static TextRenderer* displayNameRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, 12);
static TextRenderer3D* textRenderer(TextRendererType type) {
static TextRenderer3D* chatRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, 24, -1,
false, TextRenderer3D::SHADOW_EFFECT);
static TextRenderer3D* displayNameRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, 12);
switch(type) {
case CHAT:
@ -269,11 +291,28 @@ static TextRenderer* textRenderer(TextRendererType type) {
return displayNameRenderer;
}
void Avatar::render(const glm::vec3& cameraPosition, RenderArgs::RenderMode renderMode, bool postLighting) {
bool Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
auto avatarPayload = new render::Payload<AvatarData>(self);
auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload);
_renderItemID = scene->allocateID();
pendingChanges.resetItem(_renderItemID, avatarPayloadPointer);
_skeletonModel.addToScene(scene, pendingChanges);
getHead()->getFaceModel().addToScene(scene, pendingChanges);
return true;
}
void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
pendingChanges.removeItem(_renderItemID);
_skeletonModel.removeFromScene(scene, pendingChanges);
}
void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, bool postLighting) {
if (_referential) {
_referential->update();
}
auto batch = renderArgs->_batch;
if (postLighting &&
glm::distance(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(), _position) < 10.0f) {
auto geometryCache = DependencyManager::get<GeometryCache>();
@ -299,15 +338,11 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderArgs::RenderMode rend
}
if (havePosition && haveRotation) {
glPushMatrix(); {
glTranslatef(position.x, position.y, position.z);
float angle = glm::degrees(glm::angle(rotation));
glm::vec3 axis = glm::axis(rotation);
glRotatef(angle, axis.x, axis.y, axis.z);
geometryCache->renderLine(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor);
} glPopMatrix();
Transform pointerTransform;
pointerTransform.setTranslation(position);
pointerTransform.setRotation(rotation);
batch->setModelTransform(pointerTransform);
geometryCache->renderLine(*batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor);
}
}
@ -325,14 +360,11 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderArgs::RenderMode rend
}
if (havePosition && haveRotation) {
glPushMatrix(); {
glTranslatef(position.x, position.y, position.z);
float angle = glm::degrees(glm::angle(rotation));
glm::vec3 axis = glm::axis(rotation);
glRotatef(angle, axis.x, axis.y, axis.z);
geometryCache->renderLine(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor);
} glPopMatrix();
Transform pointerTransform;
pointerTransform.setTranslation(position);
pointerTransform.setRotation(rotation);
batch->setModelTransform(pointerTransform);
geometryCache->renderLine(*batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor);
}
}
}
@ -340,7 +372,7 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderArgs::RenderMode rend
// simple frustum check
float boundingRadius = getBillboardSize();
ViewFrustum* frustum = nullptr;
if (renderMode == RenderArgs::SHADOW_RENDER_MODE) {
if (renderArgs->_renderMode == RenderArgs::SHADOW_RENDER_MODE) {
frustum = Application::getInstance()->getShadowViewFrustum();
} else {
frustum = Application::getInstance()->getDisplayViewFrustum();
@ -366,14 +398,14 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderArgs::RenderMode rend
GLOW_FROM_AVERAGE_LOUDNESS = 0.0f;
}
float glowLevel = _moving && distanceToTarget > GLOW_DISTANCE && renderMode == RenderArgs::NORMAL_RENDER_MODE
float glowLevel = _moving && distanceToTarget > GLOW_DISTANCE && renderArgs->_renderMode == RenderArgs::NORMAL_RENDER_MODE
? 1.0f
: GLOW_FROM_AVERAGE_LOUDNESS;
// render body
renderBody(frustum, renderMode, postLighting, glowLevel);
renderBody(renderArgs, frustum, postLighting, glowLevel);
if (!postLighting && renderMode != RenderArgs::SHADOW_RENDER_MODE) {
if (!postLighting && renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE) {
// add local lights
const float BASE_LIGHT_DISTANCE = 2.0f;
const float LIGHT_EXPONENT = 1.0f;
@ -397,10 +429,10 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderArgs::RenderMode rend
_skeletonModel.renderJointCollisionShapes(0.7f);
}
if (renderHead && shouldRenderHead(cameraPosition, renderMode)) {
if (renderHead && shouldRenderHead(renderArgs, cameraPosition)) {
getHead()->getFaceModel().renderJointCollisionShapes(0.7f);
}
if (renderBounding && shouldRenderHead(cameraPosition, renderMode)) {
if (renderBounding && shouldRenderHead(renderArgs, cameraPosition)) {
_skeletonModel.renderBoundingCollisionShapes(0.7f);
}
@ -409,14 +441,16 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderArgs::RenderMode rend
const float LOOK_AT_INDICATOR_RADIUS = 0.03f;
const float LOOK_AT_INDICATOR_OFFSET = 0.22f;
const glm::vec4 LOOK_AT_INDICATOR_COLOR = { 0.8f, 0.0f, 0.0f, 0.75f };
glPushMatrix();
glm::vec3 position;
if (_displayName.isEmpty() || _displayNameAlpha == 0.0f) {
glTranslatef(_position.x, getDisplayNamePosition().y, _position.z);
position = glm::vec3(_position.x, getDisplayNamePosition().y, _position.z);
} else {
glTranslatef(_position.x, getDisplayNamePosition().y + LOOK_AT_INDICATOR_OFFSET, _position.z);
position = glm::vec3(_position.x, getDisplayNamePosition().y + LOOK_AT_INDICATOR_OFFSET, _position.z);
}
DependencyManager::get<GeometryCache>()->renderSphere(LOOK_AT_INDICATOR_RADIUS, 15, 15, LOOK_AT_INDICATOR_COLOR);
glPopMatrix();
Transform transform;
transform.setTranslation(position);
batch->setModelTransform(transform);
DependencyManager::get<GeometryCache>()->renderSphere(*batch, LOOK_AT_INDICATOR_RADIUS, 15, 15, LOOK_AT_INDICATOR_COLOR);
}
}
@ -437,31 +471,33 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderArgs::RenderMode rend
float angle = abs(angleBetween(toTarget + delta, toTarget - delta));
float sphereRadius = getHead()->getAverageLoudness() * SPHERE_LOUDNESS_SCALING;
if (renderMode == RenderArgs::NORMAL_RENDER_MODE && (sphereRadius > MIN_SPHERE_SIZE) &&
if (renderArgs->_renderMode == RenderArgs::NORMAL_RENDER_MODE && (sphereRadius > MIN_SPHERE_SIZE) &&
(angle < MAX_SPHERE_ANGLE) && (angle > MIN_SPHERE_ANGLE)) {
glPushMatrix();
glTranslatef(_position.x, _position.y, _position.z);
glScalef(height, height, height);
Transform transform;
transform.setTranslation(_position);
transform.setScale(height);
batch->setModelTransform(transform);
if (_voiceSphereID == GeometryCache::UNKNOWN_ID) {
_voiceSphereID = DependencyManager::get<GeometryCache>()->allocateID();
}
DependencyManager::get<GeometryCache>()->renderSphere(sphereRadius, 15, 15,
DependencyManager::get<GeometryCache>()->renderSphere(*batch, sphereRadius, 15, 15,
glm::vec4(SPHERE_COLOR[0], SPHERE_COLOR[1], SPHERE_COLOR[2], 1.0f - angle / MAX_SPHERE_ANGLE), true,
_voiceSphereID);
glPopMatrix();
}
}
}
const float DISPLAYNAME_DISTANCE = 20.0f;
setShowDisplayName(renderMode == RenderArgs::NORMAL_RENDER_MODE && distanceToTarget < DISPLAYNAME_DISTANCE);
if (!postLighting || renderMode != RenderArgs::NORMAL_RENDER_MODE || (isMyAvatar() &&
setShowDisplayName(renderArgs->_renderMode == RenderArgs::NORMAL_RENDER_MODE && distanceToTarget < DISPLAYNAME_DISTANCE);
if (renderArgs->_renderMode != RenderArgs::NORMAL_RENDER_MODE || (isMyAvatar() &&
Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_FIRST_PERSON)) {
return;
}
renderDisplayName();
renderDisplayName(renderArgs);
}
glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
@ -480,32 +516,44 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
return glm::angleAxis(angle * proportion, axis);
}
void Avatar::renderBody(ViewFrustum* renderFrustum, RenderArgs::RenderMode renderMode, bool postLighting, float glowLevel) {
Model::RenderMode modelRenderMode = renderMode;
void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool postLighting, float glowLevel) {
// check to see if when we added our models to the scene they were ready, if they were not ready, then
// fix them up in the scene
render::ScenePointer scene = Application::getInstance()->getMain3DScene();
render::PendingChanges pendingChanges;
if (_skeletonModel.needsFixupInScene()) {
_skeletonModel.removeFromScene(scene, pendingChanges);
_skeletonModel.addToScene(scene, pendingChanges);
}
if (getHead()->getFaceModel().needsFixupInScene()) {
getHead()->getFaceModel().removeFromScene(scene, pendingChanges);
getHead()->getFaceModel().addToScene(scene, pendingChanges);
}
scene->enqueuePendingChanges(pendingChanges);
{
Glower glower(glowLevel);
Glower glower(renderArgs, glowLevel);
if (_shouldRenderBillboard || !(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) {
if (postLighting || renderMode == RenderArgs::SHADOW_RENDER_MODE) {
if (postLighting || renderArgs->_renderMode == RenderArgs::SHADOW_RENDER_MODE) {
// render the billboard until both models are loaded
renderBillboard();
renderBillboard(renderArgs);
}
return;
}
if (postLighting) {
getHand()->render(false, modelRenderMode);
getHand()->render(renderArgs, false);
} else {
RenderArgs args;
args._viewFrustum = renderFrustum;
_skeletonModel.render(1.0f, modelRenderMode, &args);
renderAttachments(renderMode, &args);
// NOTE: we no longer call this here, because we've added all the model parts as renderable items in the scene
//_skeletonModel.render(renderArgs, 1.0f);
renderAttachments(renderArgs);
}
}
getHead()->render(1.0f, renderFrustum, modelRenderMode, postLighting);
getHead()->render(renderArgs, 1.0f, renderFrustum, postLighting);
}
bool Avatar::shouldRenderHead(const glm::vec3& cameraPosition, RenderArgs::RenderMode renderMode) const {
bool Avatar::shouldRenderHead(const RenderArgs* renderArgs, const glm::vec3& cameraPosition) const {
return true;
}
@ -529,19 +577,21 @@ void Avatar::simulateAttachments(float deltaTime) {
}
}
void Avatar::renderAttachments(RenderArgs::RenderMode renderMode, RenderArgs* args) {
void Avatar::renderAttachments(RenderArgs* args) {
// RenderArgs::RenderMode modelRenderMode = (renderMode == RenderArgs::SHADOW_RENDER_MODE) ?
// RenderArgs::SHADOW_RENDER_MODE : RenderArgs::DEFAULT_RENDER_MODE;
/*
foreach (Model* model, _attachmentModels) {
model->render(1.0f, renderMode, args);
model->render(args, 1.0f);
}
*/
}
void Avatar::updateJointMappings() {
// no-op; joint mappings come from skeleton model
}
void Avatar::renderBillboard() {
void Avatar::renderBillboard(RenderArgs* renderArgs) {
if (_billboard.isEmpty()) {
return;
}
@ -554,44 +604,29 @@ void Avatar::renderBillboard() {
if (!_billboardTexture->isLoaded()) {
return;
}
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.5f);
glEnable(GL_TEXTURE_2D);
glDisable(GL_LIGHTING);
glBindTexture(GL_TEXTURE_2D, _billboardTexture->getID());
glPushMatrix();
glTranslatef(_position.x, _position.y, _position.z);
// rotate about vertical to face the camera
glm::quat rotation = getOrientation();
glm::vec3 cameraVector = glm::inverse(rotation) * (Application::getInstance()->getCamera()->getPosition() - _position);
rotation = rotation * glm::angleAxis(atan2f(-cameraVector.x, -cameraVector.z), glm::vec3(0.0f, 1.0f, 0.0f));
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
// compute the size from the billboard camera parameters and scale
float size = getBillboardSize();
glScalef(size, size, size);
Transform transform;
transform.setTranslation(_position);
transform.setRotation(rotation);
transform.setScale(size);
glm::vec2 topLeft(-1.0f, -1.0f);
glm::vec2 bottomRight(1.0f, 1.0f);
glm::vec2 texCoordTopLeft(0.0f, 0.0f);
glm::vec2 texCoordBottomRight(1.0f, 1.0f);
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
gpu::Batch& batch = *renderArgs->_batch;
batch.setUniformTexture(0, _billboardTexture->getGPUTexture());
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, true);
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
glPopMatrix();
glDisable(GL_TEXTURE_2D);
glEnable(GL_LIGHTING);
glDisable(GL_ALPHA_TEST);
glBindTexture(GL_TEXTURE_2D, 0);
}
float Avatar::getBillboardSize() const {
@ -654,9 +689,10 @@ float Avatar::calculateDisplayNameScaleFactor(const glm::vec3& textPosition, boo
return scaleFactor;
}
void Avatar::renderDisplayName() {
void Avatar::renderDisplayName(RenderArgs* renderArgs) {
auto batch = renderArgs->_batch;
bool shouldShowReceiveStats = DependencyManager::get<AvatarManager>()->shouldShowReceiveStats();
bool shouldShowReceiveStats = DependencyManager::get<AvatarManager>()->shouldShowReceiveStats() && !isMyAvatar();
if ((_displayName.isEmpty() && !shouldShowReceiveStats) || _displayNameAlpha == 0.0f) {
return;
@ -665,31 +701,19 @@ void Avatar::renderDisplayName() {
// which viewing mode?
bool inHMD = Application::getInstance()->isHMDMode();
glDisable(GL_LIGHTING);
glPushMatrix();
glm::vec3 textPosition = getDisplayNamePosition();
glTranslatef(textPosition.x, textPosition.y, textPosition.z);
// we need "always facing camera": we must remove the camera rotation from the stack
glm::vec3 frontAxis(0.0f, 0.0f, 1.0f);
if (inHMD) {
glm::vec3 camPosition = Application::getInstance()->getCamera()->getPosition();
frontAxis = camPosition - textPosition;
} else {
glm::quat rotation = Application::getInstance()->getCamera()->getRotation();
frontAxis = glm::rotate(rotation, frontAxis);
}
frontAxis = glm::normalize(glm::vec3(frontAxis.z, 0.0f, -frontAxis.x));
float angle = acos(frontAxis.x) * ((frontAxis.z < 0) ? 1.0f : -1.0f);
glRotatef(glm::degrees(angle), 0.0f, 1.0f, 0.0f);
// we need "always facing camera": we must remove the camera rotation from the stac
glm::quat rotation = Application::getInstance()->getCamera()->getRotation();
// TODO: Fix scaling - at some point this or the text rendering changed in scale.
float scaleFactor = calculateDisplayNameScaleFactor(textPosition, inHMD);
glScalef(scaleFactor, -scaleFactor, scaleFactor); // TextRenderer::draw paints the text upside down in y axis
scaleFactor /= 3.5f;
Transform textTransform;
textTransform.setTranslation(textPosition);
textTransform.setRotation(rotation);
textTransform.setScale(scaleFactor);
// optionally render timing stats for this avatar with the display name
QString renderedDisplayName = _displayName;
@ -699,55 +723,45 @@ void Avatar::renderDisplayName() {
float kilobitsPerSecond = getAverageBytesReceivedPerSecond() / (float) BYTES_PER_KILOBIT;
QString statsFormat = QString("(%1 Kbps, %2 Hz)");
if (!renderedDisplayName.isEmpty()) {
statsFormat.prepend(" - ");
}
QString statsText = statsFormat.arg(QString::number(kilobitsPerSecond, 'f', 2)).arg(getReceiveRate());
glm::vec2 extent = textRenderer(DISPLAYNAME)->computeExtent(statsText);
// add the extent required for the stats to whatever was calculated for the display name
nameDynamicRect.setWidth(nameDynamicRect.width() + extent.x);
if (extent.y > nameDynamicRect.height()) {
nameDynamicRect.setHeight(extent.y);
}
renderedDisplayName += statsText;
glm::vec2 extent = textRenderer(DISPLAYNAME)->computeExtent(renderedDisplayName);
nameDynamicRect = QRect(0, 0, (int)extent.x, (int)extent.y);
}
int text_x = -nameDynamicRect.width() / 2;
int text_y = -nameDynamicRect.height() / 2;
// draw a gray background
int left = text_x + nameDynamicRect.x();
int left = text_x;
int right = left + nameDynamicRect.width();
int bottom = text_y + nameDynamicRect.y();
int bottom = text_y;
int top = bottom + nameDynamicRect.height();
const int border = 8;
bottom -= border;
left -= border;
top += border;
right += border;
// We are drawing coplanar textures with depth: need the polygon offset
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(1.0f, 1.0f);
DependencyManager::get<GeometryCache>()->renderBevelCornersRect(left, bottom, right - left, top - bottom, 3,
glm::vec4(0.2f, 0.2f, 0.2f, _displayNameAlpha * DISPLAYNAME_BACKGROUND_ALPHA / DISPLAYNAME_ALPHA));
glm::vec4 color(0.93f, 0.93f, 0.93f, _displayNameAlpha);
glm::vec4 textColor(0.93f, 0.93f, 0.93f, _displayNameAlpha);
glm::vec4 backgroundColor(0.2f, 0.2f, 0.2f,
_displayNameAlpha * DISPLAYNAME_BACKGROUND_ALPHA / DISPLAYNAME_ALPHA);
auto backgroundTransform = textTransform;
backgroundTransform.postTranslate(glm::vec3(0.0f, 0.0f, -0.001f));
batch->setModelTransform(backgroundTransform);
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(*batch);
DependencyManager::get<GeometryCache>()->renderBevelCornersRect(*batch, left, bottom, right - left, top - bottom, 3,
backgroundColor);
QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit();
glDisable(GL_POLYGON_OFFSET_FILL);
textRenderer(DISPLAYNAME)->draw(text_x, text_y, nameUTF8.data(), color);
glPopMatrix();
glEnable(GL_LIGHTING);
batch->setModelTransform(textTransform);
textRenderer(DISPLAYNAME)->draw(*batch, text_x, -text_y, nameUTF8.data(), textColor);
}
bool Avatar::findRayIntersection(RayIntersectionInfo& intersection) const {
@ -1050,13 +1064,13 @@ float Avatar::getSkeletonHeight() const {
float Avatar::getHeadHeight() const {
Extents extents = getHead()->getFaceModel().getMeshExtents();
if (!extents.isEmpty()) {
if (!extents.isEmpty() && extents.isValid()) {
return extents.maximum.y - extents.minimum.y;
}
extents = _skeletonModel.getMeshExtents();
glm::vec3 neckPosition;
if (!extents.isEmpty() && _skeletonModel.getNeckPosition(neckPosition)) {
if (!extents.isEmpty() && extents.isValid() && _skeletonModel.getNeckPosition(neckPosition)) {
return extents.maximum.y / 2.0f - neckPosition.y + _position.y;
}

View file

@ -20,12 +20,20 @@
#include <AvatarData.h>
#include <ShapeInfo.h>
#include <render/Scene.h>
#include "Hand.h"
#include "Head.h"
#include "InterfaceConfig.h"
#include "SkeletonModel.h"
#include "world.h"
namespace render {
template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar);
template <> const Item::Bound payloadGetBound(const AvatarSharedPointer& avatar);
template <> void payloadRender(const AvatarSharedPointer& avatar, RenderArgs* args);
}
static const float SCALING_RATIO = .05f;
static const float SMOOTHING_RATIO = .05f; // 0 < ratio < 1
static const float RESCALING_TOLERANCE = .02f;
@ -66,12 +74,21 @@ public:
Avatar();
~Avatar();
typedef render::Payload<AvatarData> Payload;
typedef std::shared_ptr<render::Item::PayloadInterface> PayloadPointer;
void init();
void simulate(float deltaTime);
virtual void render(const glm::vec3& cameraPosition, RenderArgs::RenderMode renderMode = RenderArgs::NORMAL_RENDER_MODE,
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPosition,
bool postLighting = false);
bool addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges);
void removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges);
//setters
void setDisplayingLookatVectors(bool displayingLookatVectors) { getHead()->setRenderLookatVectors(displayingLookatVectors); }
void setIsLookAtTarget(const bool isLookAtTarget) { _isLookAtTarget = isLookAtTarget; }
@ -87,6 +104,8 @@ public:
Hand* getHand() { return static_cast<Hand*>(_handData); }
glm::quat getWorldAlignedOrientation() const;
AABox getBounds() const;
/// Returns the distance to use as a LOD parameter.
float getLODDistance() const;
@ -212,23 +231,24 @@ protected:
glm::vec3 getDisplayNamePosition();
float calculateDisplayNameScaleFactor(const glm::vec3& textPosition, bool inHMD);
void renderDisplayName();
virtual void renderBody(ViewFrustum* renderFrustum, RenderArgs::RenderMode renderMode, bool postLighting, float glowLevel = 0.0f);
virtual bool shouldRenderHead(const glm::vec3& cameraPosition, RenderArgs::RenderMode renderMode) const;
void renderDisplayName(RenderArgs* renderArgs);
virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool postLighting, float glowLevel = 0.0f);
virtual bool shouldRenderHead(const RenderArgs* renderArgs, const glm::vec3& cameraPosition) const;
void simulateAttachments(float deltaTime);
virtual void renderAttachments(RenderArgs::RenderMode renderMode, RenderArgs* args);
virtual void renderAttachments(RenderArgs* args);
virtual void updateJointMappings();
render::ItemID _renderItemID;
private:
bool _initialized;
NetworkTexturePointer _billboardTexture;
bool _shouldRenderBillboard;
bool _isLookAtTarget;
void renderBillboard();
void renderBillboard(RenderArgs* renderArgs);
float getBillboardSize() const;

View file

@ -55,12 +55,17 @@ AvatarManager::AvatarManager(QObject* parent) :
_avatarFades() {
// register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar
qRegisterMetaType<QWeakPointer<Node> >("NodeWeakPointer");
_myAvatar = QSharedPointer<MyAvatar>(new MyAvatar());
_myAvatar = std::shared_ptr<MyAvatar>(new MyAvatar());
}
void AvatarManager::init() {
_myAvatar->init();
_avatarHash.insert(MY_AVATAR_KEY, _myAvatar);
render::ScenePointer scene = Application::getInstance()->getMain3DScene();
render::PendingChanges pendingChanges;
_myAvatar->addToScene(_myAvatar, scene, pendingChanges);
scene->enqueuePendingChanges(pendingChanges);
}
void AvatarManager::updateMyAvatar(float deltaTime) {
@ -92,9 +97,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
// simulate avatars
AvatarHash::iterator avatarIterator = _avatarHash.begin();
while (avatarIterator != _avatarHash.end()) {
Avatar* avatar = reinterpret_cast<Avatar*>(avatarIterator.value().data());
Avatar* avatar = reinterpret_cast<Avatar*>(avatarIterator.value().get());
if (avatar == _myAvatar || !avatar->isInitialized()) {
if (avatar == _myAvatar.get() || !avatar->isInitialized()) {
// DO NOT update _myAvatar! Its update has already been done earlier in the main loop.
// DO NOT update or fade out uninitialized Avatars
++avatarIterator;
@ -111,32 +116,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
simulateAvatarFades(deltaTime);
}
void AvatarManager::renderAvatars(RenderArgs::RenderMode renderMode, bool postLighting, bool selfAvatarOnly) {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::renderAvatars()");
bool renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::RenderLookAtVectors);
glm::vec3 cameraPosition = Application::getInstance()->getCamera()->getPosition();
if (!selfAvatarOnly) {
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars()) {
foreach (const AvatarSharedPointer& avatarPointer, _avatarHash) {
Avatar* avatar = static_cast<Avatar*>(avatarPointer.data());
if (!avatar->isInitialized()) {
continue;
}
avatar->render(cameraPosition, renderMode, postLighting);
avatar->setDisplayingLookatVectors(renderLookAtVectors);
}
renderAvatarFades(cameraPosition, renderMode);
}
} else {
// just render myAvatar
_myAvatar->render(cameraPosition, renderMode, postLighting);
_myAvatar->setDisplayingLookatVectors(renderLookAtVectors);
}
}
void AvatarManager::simulateAvatarFades(float deltaTime) {
QVector<AvatarSharedPointer>::iterator fadingIterator = _avatarFades.begin();
@ -144,7 +123,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
const float MIN_FADE_SCALE = 0.001f;
while (fadingIterator != _avatarFades.end()) {
Avatar* avatar = static_cast<Avatar*>(fadingIterator->data());
Avatar* avatar = static_cast<Avatar*>(fadingIterator->get());
avatar->setTargetScale(avatar->getScale() * SHRINK_RATE, true);
if (avatar->getTargetScale() < MIN_FADE_SCALE) {
fadingIterator = _avatarFades.erase(fadingIterator);
@ -155,25 +134,17 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
}
}
void AvatarManager::renderAvatarFades(const glm::vec3& cameraPosition, RenderArgs::RenderMode renderMode) {
// render avatar fades
Glower glower(renderMode == RenderArgs::NORMAL_RENDER_MODE ? 1.0f : 0.0f);
foreach(const AvatarSharedPointer& fadingAvatar, _avatarFades) {
Avatar* avatar = static_cast<Avatar*>(fadingAvatar.data());
if (avatar != static_cast<Avatar*>(_myAvatar.data()) && avatar->isInitialized()) {
avatar->render(cameraPosition, renderMode);
}
}
}
AvatarSharedPointer AvatarManager::newSharedAvatar() {
return AvatarSharedPointer(new Avatar());
}
// virtual
AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer) {
AvatarSharedPointer avatar = AvatarHashMap::addAvatar(sessionUUID, mixerWeakPointer);
std::shared_ptr<Avatar> avatar = std::dynamic_pointer_cast<Avatar>(AvatarHashMap::addAvatar(sessionUUID, mixerWeakPointer));
render::ScenePointer scene = Application::getInstance()->getMain3DScene();
render::PendingChanges pendingChanges;
avatar->addToScene(avatar, scene, pendingChanges);
scene->enqueuePendingChanges(pendingChanges);
return avatar;
}
@ -194,13 +165,16 @@ void AvatarManager::removeAvatarMotionState(Avatar* avatar) {
void AvatarManager::removeAvatar(const QUuid& sessionUUID) {
AvatarHash::iterator avatarIterator = _avatarHash.find(sessionUUID);
if (avatarIterator != _avatarHash.end()) {
Avatar* avatar = reinterpret_cast<Avatar*>(avatarIterator.value().data());
std::shared_ptr<Avatar> avatar = std::dynamic_pointer_cast<Avatar>(avatarIterator.value());
if (avatar != _myAvatar && avatar->isInitialized()) {
removeAvatarMotionState(avatar);
removeAvatarMotionState(avatar.get());
_avatarFades.push_back(avatarIterator.value());
_avatarHash.erase(avatarIterator);
}
render::ScenePointer scene = Application::getInstance()->getMain3DScene();
render::PendingChanges pendingChanges;
avatar->removeFromScene(avatar, scene, pendingChanges);
scene->enqueuePendingChanges(pendingChanges);
}
}
@ -208,8 +182,8 @@ void AvatarManager::clearOtherAvatars() {
// clear any avatars that came from an avatar-mixer
AvatarHash::iterator avatarIterator = _avatarHash.begin();
while (avatarIterator != _avatarHash.end()) {
Avatar* avatar = reinterpret_cast<Avatar*>(avatarIterator.value().data());
if (avatar == _myAvatar || !avatar->isInitialized()) {
Avatar* avatar = reinterpret_cast<Avatar*>(avatarIterator.value().get());
if (avatar == _myAvatar.get() || !avatar->isInitialized()) {
// don't remove myAvatar or uninitialized avatars from the list
++avatarIterator;
} else {
@ -276,7 +250,7 @@ void AvatarManager::handleCollisionEvents(CollisionEvents& collisionEvents) {
void AvatarManager::updateAvatarPhysicsShape(const QUuid& id) {
AvatarHash::iterator avatarItr = _avatarHash.find(id);
if (avatarItr != _avatarHash.end()) {
Avatar* avatar = static_cast<Avatar*>(avatarItr.value().data());
Avatar* avatar = static_cast<Avatar*>(avatarItr.value().get());
AvatarMotionState* motionState = avatar->_motionState;
if (motionState) {
motionState->addDirtyFlags(EntityItem::DIRTY_SHAPE);

View file

@ -35,11 +35,10 @@ public:
void init();
MyAvatar* getMyAvatar() { return _myAvatar.data(); }
MyAvatar* getMyAvatar() { return _myAvatar.get(); }
void updateMyAvatar(float deltaTime);
void updateOtherAvatars(float deltaTime);
void renderAvatars(RenderArgs::RenderMode renderMode, bool postLighting = false, bool selfAvatarOnly = false);
void clearOtherAvatars();
@ -70,7 +69,6 @@ private:
AvatarManager(const AvatarManager& other);
void simulateAvatarFades(float deltaTime);
void renderAvatarFades(const glm::vec3& cameraPosition, RenderArgs::RenderMode renderMode);
// virtual overrides
virtual AvatarSharedPointer newSharedAvatar();
@ -79,7 +77,7 @@ private:
virtual void removeAvatar(const QUuid& sessionUUID);
QVector<AvatarSharedPointer> _avatarFades;
QSharedPointer<MyAvatar> _myAvatar;
std::shared_ptr<MyAvatar> _myAvatar;
quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate.
QVector<AvatarManager::LocalLight> _localLights;

View file

@ -39,7 +39,8 @@ void FaceModel::simulate(float deltaTime, bool fullUpdate) {
setPupilDilation(_owningHead->getPupilDilation());
setBlendshapeCoefficients(_owningHead->getBlendshapeCoefficients());
invalidCalculatedMeshBoxes();
// FIXME - this is very expensive, we shouldn't do it if we don't have to
//invalidCalculatedMeshBoxes();
if (isActive()) {
setOffset(-_geometry->getFBXGeometry().neckPivot);

View file

@ -102,8 +102,8 @@ void Hand::resolvePenetrations() {
}
}
void Hand::render(bool isMine, Model::RenderMode renderMode) {
if (renderMode != RenderArgs::SHADOW_RENDER_MODE &&
void Hand::render(RenderArgs* renderArgs, bool isMine) {
if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE &&
Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes)) {
// draw a green sphere at hand joint location, which is actually near the wrist)
for (size_t i = 0; i < getNumPalms(); i++) {
@ -119,7 +119,7 @@ void Hand::render(bool isMine, Model::RenderMode renderMode) {
}
}
if (renderMode != RenderArgs::SHADOW_RENDER_MODE && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHands)) {
if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHands)) {
renderHandTargets(isMine);
}

View file

@ -40,19 +40,8 @@ class Hand : public HandData {
public:
Hand(Avatar* owningAvatar);
struct HandBall
{
glm::vec3 position; // the actual dynamic position of the ball at any given time
glm::quat rotation; // the rotation of the ball
glm::vec3 velocity; // the velocity of the ball
float radius; // the radius of the ball
bool isCollidable; // whether or not the ball responds to collisions
bool isColliding; // ball is currently colliding
float touchForce; // a scalar determining the amount that the cursor (or hand) is penetrating the ball
};
void simulate(float deltaTime, bool isMine);
void render(bool isMine, Model::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE);
void render(RenderArgs* renderArgs, bool isMine);
void collideAgainstAvatar(Avatar* avatar, bool isMyHand);

View file

@ -292,15 +292,11 @@ void Head::relaxLean(float deltaTime) {
_deltaLeanForward *= relaxationFactor;
}
void Head::render(float alpha, ViewFrustum* renderFrustum, Model::RenderMode mode, bool postLighting) {
void Head::render(RenderArgs* renderArgs, float alpha, ViewFrustum* renderFrustum, bool postLighting) {
if (postLighting) {
if (_renderLookatVectors) {
renderLookatVectors(_leftEyePosition, _rightEyePosition, getCorrectedLookAtPosition());
renderLookatVectors(renderArgs, _leftEyePosition, _rightEyePosition, getCorrectedLookAtPosition());
}
} else {
RenderArgs args;
args._viewFrustum = renderFrustum;
_faceModel.render(alpha, mode, &args);
}
}
@ -383,9 +379,9 @@ void Head::addLeanDeltas(float sideways, float forward) {
_deltaLeanForward += forward;
}
void Head::renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition) {
void Head::renderLookatVectors(RenderArgs* renderArgs, glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition) {
auto geometryCache = DependencyManager::get<GeometryCache>();
DependencyManager::get<GlowEffect>()->begin();
DependencyManager::get<GlowEffect>()->begin(renderArgs);
glLineWidth(2.0);
@ -394,7 +390,7 @@ void Head::renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosi
geometryCache->renderLine(leftEyePosition, lookatPosition, startColor, endColor, _leftEyeLookAtID);
geometryCache->renderLine(rightEyePosition, lookatPosition, startColor, endColor, _rightEyeLookAtID);
DependencyManager::get<GlowEffect>()->end();
DependencyManager::get<GlowEffect>()->end(renderArgs);
}

View file

@ -40,7 +40,7 @@ public:
void init();
void reset();
void simulate(float deltaTime, bool isMine, bool billboard = false);
void render(float alpha, ViewFrustum* renderFrustum, Model::RenderMode mode, bool postLighting);
void render(RenderArgs* renderArgs, float alpha, ViewFrustum* renderFrustum, bool postLighting);
void setScale(float scale);
void setPosition(glm::vec3 position) { _position = position; }
void setAverageLoudness(float averageLoudness) { _averageLoudness = averageLoudness; }
@ -153,7 +153,7 @@ private:
int _rightEyeLookAtID;
// private methods
void renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition);
void renderLookatVectors(RenderArgs* renderArgs, glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition);
void calculateMouthShapes();
void applyEyelidOffset(glm::quat headOrientation);

View file

@ -45,6 +45,9 @@
#include "Util.h"
#include "InterfaceLogging.h"
#include "gpu/GLBackend.h"
using namespace std;
const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f);
@ -105,7 +108,7 @@ MyAvatar::MyAvatar() :
}
MyAvatar::~MyAvatar() {
_lookAtTargetAvatar.clear();
_lookAtTargetAvatar.reset();
}
QByteArray MyAvatar::toByteArray() {
@ -328,14 +331,14 @@ void MyAvatar::renderDebugBodyPoints() {
}
// virtual
void MyAvatar::render(const glm::vec3& cameraPosition, RenderArgs::RenderMode renderMode, bool postLighting) {
void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, bool postLighting) {
// don't render if we've been asked to disable local rendering
if (!_shouldRender) {
return; // exit early
}
Avatar::render(cameraPosition, renderMode, postLighting);
Avatar::render(renderArgs, cameraPosition, postLighting);
// don't display IK constraints in shadow mode
if (Menu::getInstance()->isOptionChecked(MenuOption::ShowIKConstraints) && postLighting) {
_skeletonModel.renderIKConstraints();
@ -856,7 +859,7 @@ void MyAvatar::updateLookAtTargetAvatar() {
//
// Look at the avatar whose eyes are closest to the ray in direction of my avatar's head
//
_lookAtTargetAvatar.clear();
_lookAtTargetAvatar.reset();
_targetAvatarPosition = glm::vec3(0.0f);
glm::vec3 lookForward = getHead()->getFinalOrientationInWorldFrame() * IDENTITY_FRONT;
@ -868,7 +871,7 @@ void MyAvatar::updateLookAtTargetAvatar() {
int howManyLookingAtMe = 0;
foreach (const AvatarSharedPointer& avatarPointer, DependencyManager::get<AvatarManager>()->getAvatarHash()) {
Avatar* avatar = static_cast<Avatar*>(avatarPointer.data());
Avatar* avatar = static_cast<Avatar*>(avatarPointer.get());
bool isCurrentTarget = avatar->getIsLookAtTarget();
float distanceTo = glm::length(avatar->getHead()->getEyePosition() - cameraPosition);
avatar->setIsLookAtTarget(false);
@ -896,13 +899,14 @@ void MyAvatar::updateLookAtTargetAvatar() {
}
}
}
if (_lookAtTargetAvatar) {
static_cast<Avatar*>(_lookAtTargetAvatar.data())->setIsLookAtTarget(true);
auto avatarPointer = _lookAtTargetAvatar.lock();
if (avatarPointer) {
static_cast<Avatar*>(avatarPointer.get())->setIsLookAtTarget(true);
}
}
void MyAvatar::clearLookAtTargetAvatar() {
_lookAtTargetAvatar.clear();
_lookAtTargetAvatar.reset();
}
bool MyAvatar::isLookingAtLeftEye() {
@ -1166,11 +1170,26 @@ void MyAvatar::attach(const QString& modelURL, const QString& jointName, const g
Avatar::attach(modelURL, jointName, translation, rotation, scale, allowDuplicates, useSaved);
}
void MyAvatar::renderBody(ViewFrustum* renderFrustum, RenderArgs::RenderMode renderMode, bool postLighting, float glowLevel) {
void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool postLighting, float glowLevel) {
if (!(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) {
return; // wait until both models are loaded
}
// check to see if when we added our models to the scene they were ready, if they were not ready, then
// fix them up in the scene
render::ScenePointer scene = Application::getInstance()->getMain3DScene();
render::PendingChanges pendingChanges;
if (_skeletonModel.needsFixupInScene()) {
_skeletonModel.removeFromScene(scene, pendingChanges);
_skeletonModel.addToScene(scene, pendingChanges);
}
if (getHead()->getFaceModel().needsFixupInScene()) {
getHead()->getFaceModel().removeFromScene(scene, pendingChanges);
getHead()->getFaceModel().addToScene(scene, pendingChanges);
}
scene->enqueuePendingChanges(pendingChanges);
Camera *camera = Application::getInstance()->getCamera();
const glm::vec3 cameraPos = camera->getPosition();
@ -1190,28 +1209,27 @@ void MyAvatar::renderBody(ViewFrustum* renderFrustum, RenderArgs::RenderMode ren
}*/
// Render the body's voxels and head
RenderArgs::RenderMode modelRenderMode = renderMode;
if (!postLighting) {
RenderArgs args;
args._viewFrustum = renderFrustum;
_skeletonModel.render(1.0f, modelRenderMode, &args);
renderAttachments(renderMode, &args);
// NOTE: we no longer call this here, because we've added all the model parts as renderable items in the scene
//_skeletonModel.render(renderArgs, 1.0f);
renderAttachments(renderArgs);
}
// Render head so long as the camera isn't inside it
if (shouldRenderHead(cameraPos, renderMode)) {
getHead()->render(1.0f, renderFrustum, modelRenderMode, postLighting);
if (shouldRenderHead(renderArgs, cameraPos)) {
getHead()->render(renderArgs, 1.0f, renderFrustum, postLighting);
}
if (postLighting) {
getHand()->render(true, modelRenderMode);
getHand()->render(renderArgs, true);
}
}
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.50f;
bool MyAvatar::shouldRenderHead(const glm::vec3& cameraPosition, RenderArgs::RenderMode renderMode) const {
bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs, const glm::vec3& cameraPosition) const {
const Head* head = getHead();
return (renderMode != RenderArgs::NORMAL_RENDER_MODE) || (Application::getInstance()->getCamera()->getMode() != CAMERA_MODE_FIRST_PERSON) ||
return (renderArgs->_renderMode != RenderArgs::NORMAL_RENDER_MODE) || (Application::getInstance()->getCamera()->getMode() != CAMERA_MODE_FIRST_PERSON) ||
(glm::length(cameraPosition - head->getEyePosition()) > RENDER_HEAD_CUTOFF_DISTANCE * _scale);
}
@ -1474,7 +1492,9 @@ void MyAvatar::maybeUpdateBillboard() {
return;
}
}
QImage image = Application::getInstance()->renderAvatarBillboard();
gpu::Context context(new gpu::GLBackend());
RenderArgs renderArgs(&context);
QImage image = Application::getInstance()->renderAvatarBillboard(&renderArgs);
_billboard.clear();
QBuffer buffer(&_billboard);
buffer.open(QIODevice::WriteOnly);
@ -1551,21 +1571,25 @@ void MyAvatar::updateMotionBehavior() {
_feetTouchFloor = menu->isOptionChecked(MenuOption::ShiftHipsForIdleAnimations);
}
void MyAvatar::renderAttachments(RenderArgs::RenderMode renderMode, RenderArgs* args) {
if (Application::getInstance()->getCamera()->getMode() != CAMERA_MODE_FIRST_PERSON || renderMode == RenderArgs::MIRROR_RENDER_MODE) {
Avatar::renderAttachments(renderMode, args);
void MyAvatar::renderAttachments(RenderArgs* args) {
if (Application::getInstance()->getCamera()->getMode() != CAMERA_MODE_FIRST_PERSON || args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
Avatar::renderAttachments(args);
return;
}
const FBXGeometry& geometry = _skeletonModel.getGeometry()->getFBXGeometry();
QString headJointName = (geometry.headJointIndex == -1) ? QString() : geometry.joints.at(geometry.headJointIndex).name;
// RenderArgs::RenderMode modelRenderMode = (renderMode == RenderArgs::SHADOW_RENDER_MODE) ?
// RenderArgs::SHADOW_RENDER_MODE : RenderArgs::DEFAULT_RENDER_MODE;
// FIX ME - attachments need to be added to scene too...
/*
for (int i = 0; i < _attachmentData.size(); i++) {
const QString& jointName = _attachmentData.at(i).jointName;
if (jointName != headJointName && jointName != "Head") {
_attachmentModels.at(i)->render(1.0f, renderMode, args);
_attachmentModels.at(i)->render(args, 1.0f);
}
}
*/
}
//Renders sixense laser pointers for UI selection with controllers

View file

@ -37,9 +37,9 @@ public:
void simulate(float deltaTime);
void updateFromTrackers(float deltaTime);
void render(const glm::vec3& cameraPosition, RenderArgs::RenderMode renderMode = RenderArgs::NORMAL_RENDER_MODE, bool postLighting = false);
void renderBody(ViewFrustum* renderFrustum, RenderArgs::RenderMode renderMode, bool postLighting, float glowLevel = 0.0f);
bool shouldRenderHead(const glm::vec3& cameraPosition, RenderArgs::RenderMode renderMode) const;
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, bool postLighting = false) override;
virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool postLighting, float glowLevel = 0.0f) override;
virtual bool shouldRenderHead(const RenderArgs* renderArgs, const glm::vec3& cameraPosition) const override;
void renderDebugBodyPoints();
// setters
@ -108,7 +108,7 @@ public:
Q_INVOKABLE glm::vec3 getEyePosition() const { return getHead()->getEyePosition(); }
Q_INVOKABLE glm::vec3 getTargetAvatarPosition() const { return _targetAvatarPosition; }
QWeakPointer<AvatarData> getLookAtTargetAvatar() const { return _lookAtTargetAvatar; }
AvatarWeakPointer getLookAtTargetAvatar() const { return _lookAtTargetAvatar; }
void updateLookAtTargetAvatar();
void clearLookAtTargetAvatar();
@ -198,7 +198,7 @@ signals:
void transformChanged();
protected:
virtual void renderAttachments(RenderArgs::RenderMode renderMode, RenderArgs* args);
virtual void renderAttachments(RenderArgs* args);
private:
@ -226,7 +226,7 @@ private:
DynamicCharacterController _characterController;
QWeakPointer<AvatarData> _lookAtTargetAvatar;
AvatarWeakPointer _lookAtTargetAvatar;
glm::vec3 _targetAvatarPosition;
bool _shouldRender;
bool _billboardValid;

View file

@ -255,9 +255,12 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
}
void SkeletonModel::updateJointState(int index) {
if (index > _jointStates.size()) {
return; // bail
}
JointState& state = _jointStates[index];
const FBXJoint& joint = state.getFBXJoint();
if (joint.parentIndex != -1) {
if (joint.parentIndex != -1 && joint.parentIndex <= _jointStates.size()) {
const JointState& parentState = _jointStates.at(joint.parentIndex);
const FBXGeometry& geometry = _geometry->getFBXGeometry();
if (index == geometry.leanJointIndex) {

View file

@ -76,10 +76,10 @@ void CameraToolBox::render(int x, int y, bool boxed) {
glEnable(GL_TEXTURE_2D);
if (!_enabledTexture) {
_enabledTexture = DependencyManager::get<TextureCache>()->getImageTexture(PathUtils::resourcesPath() + "images/face.svg");
_enabledTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/face.svg");
}
if (!_mutedTexture) {
_mutedTexture = DependencyManager::get<TextureCache>()->getImageTexture(PathUtils::resourcesPath() + "images/face-mute.svg");
_mutedTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/face-mute.svg");
}
const int MUTE_ICON_SIZE = 24;

View file

@ -14,7 +14,18 @@
#include "DeviceTracker.h"
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-compare"
#endif
#include <glm/glm.hpp>
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#include <glm/gtc/quaternion.hpp>
#include <glm/gtc/matrix_transform.hpp>

View file

@ -465,7 +465,7 @@ void OculusManager::configureCamera(Camera& camera) {
}
//Displays everything for the oculus, frame timing must be active
void OculusManager::display(QGLWidget * glCanvas, const glm::quat &bodyOrientation, const glm::vec3 &position, Camera& whichCamera) {
void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const glm::quat &bodyOrientation, const glm::vec3 &position, Camera& whichCamera) {
#ifdef DEBUG
// Ensure the frame counter always increments by exactly 1
@ -532,7 +532,7 @@ void OculusManager::display(QGLWidget * glCanvas, const glm::quat &bodyOrientati
//Bind our framebuffer object. If we are rendering the glow effect, we let the glow effect shader take care of it
if (Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect)) {
DependencyManager::get<GlowEffect>()->prepare();
DependencyManager::get<GlowEffect>()->prepare(renderArgs);
} else {
auto primaryFBO = DependencyManager::get<TextureCache>()->getPrimaryFramebuffer();
glBindFramebuffer(GL_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(primaryFBO));
@ -613,7 +613,8 @@ void OculusManager::display(QGLWidget * glCanvas, const glm::quat &bodyOrientati
glViewport(vp.Pos.x, vp.Pos.y, vp.Size.w, vp.Size.h);
qApp->displaySide(*_camera, false, RenderArgs::MONO);
renderArgs->_renderSide = RenderArgs::MONO;
qApp->displaySide(renderArgs, *_camera, false);
qApp->getApplicationOverlay().displayOverlayTextureHmd(*_camera);
});
_activeEye = ovrEye_Count;
@ -625,7 +626,7 @@ void OculusManager::display(QGLWidget * glCanvas, const glm::quat &bodyOrientati
if (Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect)) {
//Full texture viewport for glow effect
glViewport(0, 0, _renderTargetSize.w, _renderTargetSize.h);
finalFbo = DependencyManager::get<GlowEffect>()->render();
finalFbo = DependencyManager::get<GlowEffect>()->render(renderArgs);
} else {
finalFbo = DependencyManager::get<TextureCache>()->getPrimaryFramebuffer();
glBindFramebuffer(GL_FRAMEBUFFER, 0);

View file

@ -20,6 +20,8 @@
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "RenderArgs.h"
class Camera;
class PalmData;
class Text3DOverlay;
@ -61,7 +63,7 @@ public:
static void endFrameTiming();
static bool allowSwap();
static void configureCamera(Camera& camera);
static void display(QGLWidget * glCanvas, const glm::quat &bodyOrientation, const glm::vec3 &position, Camera& whichCamera);
static void display(QGLWidget * glCanvas, RenderArgs* renderArgs, const glm::quat &bodyOrientation, const glm::vec3 &position, Camera& whichCamera);
static void reset();
/// param \yaw[out] yaw in radians

View file

@ -80,7 +80,7 @@ void TV3DManager::configureCamera(Camera& whichCamera, int screenWidth, int scre
glLoadIdentity();
}
void TV3DManager::display(Camera& whichCamera) {
void TV3DManager::display(RenderArgs* renderArgs, Camera& whichCamera) {
double nearZ = DEFAULT_NEAR_CLIP; // near clipping plane
double farZ = DEFAULT_FAR_CLIP; // far clipping plane
@ -93,7 +93,7 @@ void TV3DManager::display(Camera& whichCamera) {
int portalH = deviceSize.height();
DependencyManager::get<GlowEffect>()->prepare();
DependencyManager::get<GlowEffect>()->prepare(renderArgs);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
Camera eyeCamera;
@ -118,7 +118,8 @@ void TV3DManager::display(Camera& whichCamera) {
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
qApp->displaySide(eyeCamera, false, RenderArgs::MONO);
renderArgs->_renderSide = RenderArgs::MONO;
qApp->displaySide(renderArgs, eyeCamera, false);
qApp->getApplicationOverlay().displayOverlayTextureStereo(whichCamera, _aspect, fov);
_activeEye = NULL;
}, [&]{
@ -128,7 +129,7 @@ void TV3DManager::display(Camera& whichCamera) {
glPopMatrix();
glDisable(GL_SCISSOR_TEST);
auto finalFbo = DependencyManager::get<GlowEffect>()->render();
auto finalFbo = DependencyManager::get<GlowEffect>()->render(renderArgs);
auto fboSize = finalFbo->getSize();
// Get the ACTUAL device size for the BLIT
deviceSize = qApp->getDeviceSize();

View file

@ -33,7 +33,7 @@ public:
static void connect();
static bool isConnected();
static void configureCamera(Camera& camera, int screenWidth, int screenHeight);
static void display(Camera& whichCamera);
static void display(RenderArgs* renderArgs, Camera& whichCamera);
static void overrideOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal,
float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane);
private:

View file

@ -37,8 +37,8 @@ OctreeFade::OctreeFade(FadeDirection direction, float red, float green, float bl
opacity = (direction == FADE_OUT) ? FADE_OUT_START : FADE_IN_START;
}
void OctreeFade::render() {
DependencyManager::get<GlowEffect>()->begin();
void OctreeFade::render(RenderArgs* renderArgs) {
DependencyManager::get<GlowEffect>()->begin(renderArgs);
glDisable(GL_LIGHTING);
glPushMatrix();
@ -53,7 +53,7 @@ void OctreeFade::render() {
glEnable(GL_LIGHTING);
DependencyManager::get<GlowEffect>()->end();
DependencyManager::get<GlowEffect>()->end(renderArgs);
opacity *= (direction == FADE_OUT) ? FADE_OUT_STEP : FADE_IN_STEP;
}

View file

@ -39,7 +39,7 @@ public:
OctreeFade(FadeDirection direction = FADE_OUT, float red = DEFAULT_RED,
float green = DEFAULT_GREEN, float blue = DEFAULT_BLUE);
void render();
void render(RenderArgs* renderArgs);
bool isDone() const;
};

View file

@ -185,7 +185,7 @@ ApplicationOverlay::~ApplicationOverlay() {
}
// Renders the overlays either to a texture or to the screen
void ApplicationOverlay::renderOverlay() {
void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()");
Overlays& overlays = qApp->getOverlays();
@ -222,7 +222,7 @@ void ApplicationOverlay::renderOverlay() {
// give external parties a change to hook in
emit qApp->renderingOverlay();
overlays.renderHUD();
overlays.renderHUD(renderArgs);
renderPointers();
@ -423,8 +423,8 @@ void ApplicationOverlay::displayOverlayTextureStereo(Camera& whichCamera, float
});
if (!_crosshairTexture) {
_crosshairTexture = DependencyManager::get<TextureCache>()->
getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png");
_crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() +
"images/sixense-reticle.png");
}
//draw the mouse pointer
@ -554,8 +554,7 @@ bool ApplicationOverlay::calculateRayUICollisionPoint(const glm::vec3& position,
void ApplicationOverlay::renderPointers() {
//lazily load crosshair texture
if (_crosshairTexture == 0) {
_crosshairTexture = DependencyManager::get<TextureCache>()->
getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png");
_crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png");
}
glEnable(GL_TEXTURE_2D);

View file

@ -32,7 +32,7 @@ public:
ApplicationOverlay();
~ApplicationOverlay();
void renderOverlay();
void renderOverlay(RenderArgs* renderArgs);
void displayOverlayTexture();
void displayOverlayTextureStereo(Camera& whichCamera, float aspectRatio, float fov);
void displayOverlayTextureHmd(Camera& whichCamera);

View file

@ -34,11 +34,10 @@ RearMirrorTools::RearMirrorTools(QRect& bounds) :
_windowed(false),
_fullScreen(false)
{
auto textureCache = DependencyManager::get<TextureCache>();
_closeTexture = textureCache->getImageTexture(PathUtils::resourcesPath() + "images/close.svg");
_closeTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/close.svg");
_zoomHeadTexture = textureCache->getImageTexture(PathUtils::resourcesPath() + "images/plus.svg");
_zoomBodyTexture = textureCache->getImageTexture(PathUtils::resourcesPath() + "images/minus.svg");
_zoomHeadTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/plus.svg");
_zoomBodyTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/minus.svg");
_shrinkIconRect = QRect(ICON_PADDING, ICON_PADDING, ICON_SIZE, ICON_SIZE);
_closeIconRect = QRect(_bounds.left() + ICON_PADDING, _bounds.top() + ICON_PADDING, ICON_SIZE, ICON_SIZE);
@ -47,7 +46,7 @@ RearMirrorTools::RearMirrorTools(QRect& bounds) :
_headZoomIconRect = QRect(_bounds.left() + ICON_PADDING, _bounds.bottom() - ICON_PADDING - ICON_SIZE, ICON_SIZE, ICON_SIZE);
}
void RearMirrorTools::render(bool fullScreen, const QPoint & mousePosition) {
void RearMirrorTools::render(RenderArgs* renderArgs, bool fullScreen, const QPoint & mousePosition) {
if (fullScreen) {
_fullScreen = true;
displayIcon(QRect(QPoint(), qApp->getDeviceSize()), _shrinkIconRect, _closeTexture);

View file

@ -24,7 +24,7 @@ class RearMirrorTools : public QObject {
Q_OBJECT
public:
RearMirrorTools(QRect& bounds);
void render(bool fullScreen, const QPoint & mousePos);
void render(RenderArgs* renderArgs, bool fullScreen, const QPoint & mousePos);
bool mousePressEvent(int x, int y);
static Setting::Handle<int> rearViewZoomLevel;

View file

@ -467,31 +467,30 @@ void Stats::display(
horizontalOffset += 5;
// Model/Entity render details
EntityTreeRenderer* entities = Application::getInstance()->getEntities();
octreeStats.str("");
octreeStats << "Entity Items rendered: " << entities->getItemsRendered()
<< " / Out of view:" << entities->getItemsOutOfView()
<< " / Too small:" << entities->getItemsTooSmall();
octreeStats << "Triangles: " << _renderDetails._trianglesRendered
<< " / Quads:" << _renderDetails._quadsRendered
<< " / Material Switches:" << _renderDetails._materialSwitches;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
if (_expanded) {
octreeStats.str("");
octreeStats << " Meshes rendered: " << entities->getMeshesRendered()
<< " / Out of view:" << entities->getMeshesOutOfView()
<< " / Too small:" << entities->getMeshesTooSmall();
octreeStats << " Mesh Parts Rendered Opaque: " << _renderDetails._opaque._rendered
<< " / Translucent:" << _renderDetails._translucent._rendered;
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
octreeStats.str("");
octreeStats << " Triangles: " << entities->getTrianglesRendered()
<< " / Quads:" << entities->getQuadsRendered()
<< " / Material Switches:" << entities->getMaterialSwitches();
octreeStats << " Opaque considered: " << _renderDetails._opaque._considered
<< " / Out of view:" << _renderDetails._opaque._outOfView
<< " / Too small:" << _renderDetails._opaque._tooSmall;
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
octreeStats.str("");
octreeStats << " Mesh Parts Rendered Opaque: " << entities->getOpaqueMeshPartsRendered()
<< " / Translucent:" << entities->getTranslucentMeshPartsRendered();
octreeStats << " Translucent considered: " << _renderDetails._translucent._considered
<< " / Out of view:" << _renderDetails._translucent._outOfView
<< " / Too small:" << _renderDetails._translucent._tooSmall;
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
}

View file

@ -14,7 +14,7 @@
#include <QObject>
#include <NodeList.h>
#include <RenderArgs.h>
class Stats: public QObject {
Q_OBJECT
@ -35,6 +35,8 @@ public:
int inKbitsPerSecond, int outKbitsPerSecond, int voxelPacketsToProcess);
bool includeTimingRecord(const QString& name);
void setRenderDetails(const RenderDetails& details) { _renderDetails = details; }
private:
static Stats* _sharedInstance;
@ -51,6 +53,7 @@ private:
int _lastHorizontalOffset;
RenderDetails _renderDetails;
};
#endif // hifi_Stats_h

View file

@ -49,6 +49,11 @@ Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) :
Base3DOverlay::~Base3DOverlay() {
}
// TODO: Implement accurate getBounds() implementations
AABox Base3DOverlay::getBounds() const {
return AABox(_position, glm::vec3(1.0f));
}
void Base3DOverlay::setProperties(const QScriptValue& properties) {
Overlay::setProperties(properties);

View file

@ -49,6 +49,8 @@ public:
void setDrawInFront(bool value) { _drawInFront = value; }
void setDrawOnHUD(bool value) { _drawOnHUD = value; }
virtual AABox getBounds() const;
virtual void setProperties(const QScriptValue& properties);
virtual QScriptValue getProperty(const QString& property);

View file

@ -126,7 +126,7 @@ void Cube3DOverlay::render(RenderArgs* args) {
} else {
glScalef(dimensions.x, dimensions.y, dimensions.z);
DependencyManager::get<DeferredLightingEffect>()->renderWireCube(1.0f, cubeColor);
DependencyManager::get<GeometryCache>()->renderWireCube(1.0f, cubeColor);
}
}
glPopMatrix();

View file

@ -47,7 +47,7 @@ void LocalModelsOverlay::render(RenderArgs* args) {
Application* app = Application::getInstance();
glm::vec3 oldTranslation = app->getViewMatrixTranslation();
app->setViewMatrixTranslation(oldTranslation + _position);
_entityTreeRenderer->render();
_entityTreeRenderer->render(args);
Application::getInstance()->setViewMatrixTranslation(oldTranslation);
} glPopMatrix();

View file

@ -9,6 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <Application.h>
#include <GlowEffect.h>
#include "ModelOverlay.h"
@ -54,11 +55,34 @@ void ModelOverlay::update(float deltatime) {
_isLoaded = _model.isActive();
}
bool ModelOverlay::addToScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
Base3DOverlay::addToScene(overlay, scene, pendingChanges);
_model.addToScene(scene, pendingChanges);
return true;
}
void ModelOverlay::removeFromScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
Base3DOverlay::removeFromScene(overlay, scene, pendingChanges);
_model.removeFromScene(scene, pendingChanges);
}
void ModelOverlay::render(RenderArgs* args) {
// check to see if when we added our model to the scene they were ready, if they were not ready, then
// fix them up in the scene
render::ScenePointer scene = Application::getInstance()->getMain3DScene();
render::PendingChanges pendingChanges;
if (_model.needsFixupInScene()) {
_model.removeFromScene(scene, pendingChanges);
_model.addToScene(scene, pendingChanges);
}
scene->enqueuePendingChanges(pendingChanges);
if (!_visible) {
return;
}
/*
if (_model.isActive()) {
if (_model.isRenderable()) {
float glowLevel = getGlowLevel();
@ -66,12 +90,13 @@ void ModelOverlay::render(RenderArgs* args) {
if (glowLevel > 0.0f) {
glower = new Glower(glowLevel);
}
_model.render(getAlpha(), RenderArgs::DEFAULT_RENDER_MODE, args);
_model.render(args, getAlpha());
if (glower) {
delete glower;
}
}
}
*/
}
void ModelOverlay::setProperties(const QScriptValue &properties) {

View file

@ -32,6 +32,9 @@ public:
virtual ModelOverlay* createClone() const;
virtual bool addToScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
virtual void removeFromScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
private:
Model _model;

View file

@ -16,6 +16,7 @@
#include <NumericalConstants.h>
Overlay::Overlay() :
_renderItemID(render::Item::INVALID_ITEM_ID),
_isLoaded(true),
_alpha(DEFAULT_ALPHA),
_glowLevel(0.0f),
@ -35,6 +36,7 @@ Overlay::Overlay() :
}
Overlay::Overlay(const Overlay* overlay) :
_renderItemID(render::Item::INVALID_ITEM_ID),
_isLoaded(overlay->_isLoaded),
_alpha(overlay->_alpha),
_glowLevel(overlay->_glowLevel),
@ -225,3 +227,16 @@ float Overlay::updatePulse() {
return _pulse;
}
bool Overlay::addToScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
auto overlayPayload = new Overlay::Payload(overlay);
auto overlayPayloadPointer = Overlay::PayloadPointer(overlayPayload);
_renderItemID = scene->allocateID();
pendingChanges.resetItem(_renderItemID, overlayPayloadPointer);
return true;
}
void Overlay::removeFromScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
pendingChanges.removeItem(_renderItemID);
}

View file

@ -21,6 +21,8 @@
#include <RegisteredMetaTypes.h>
#include <SharedUtil.h> // for xColor
#include <RenderArgs.h>
#include <AABox.h>
#include <render/Scene.h>
const xColor DEFAULT_OVERLAY_COLOR = { 255, 255, 255 };
const float DEFAULT_ALPHA = 0.7f;
@ -33,7 +35,12 @@ public:
NO_ANCHOR,
MY_AVATAR
};
typedef std::shared_ptr<Overlay> Pointer;
typedef render::Payload<Overlay> Payload;
typedef std::shared_ptr<render::Item::PayloadInterface> PayloadPointer;
Overlay();
Overlay(const Overlay* overlay);
~Overlay();
@ -41,6 +48,9 @@ public:
virtual void update(float deltatime) {}
virtual void render(RenderArgs* args) = 0;
virtual bool addToScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
virtual void removeFromScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
// getters
virtual bool is3D() const = 0;
bool isLoaded() { return _isLoaded; }
@ -50,7 +60,6 @@ public:
float getGlowLevel();
Anchor getAnchor() const { return _anchor; }
float getPulseMax() const { return _pulseMax; }
float getPulseMin() const { return _pulseMin; }
float getPulsePeriod() const { return _pulsePeriod; }
@ -81,9 +90,14 @@ public:
virtual Overlay* createClone() const = 0;
virtual QScriptValue getProperty(const QString& property);
render::ItemID getRenderItemID() const { return _renderItemID; }
void setRenderItemID(render::ItemID renderItemID) { _renderItemID = renderItemID; }
protected:
float updatePulse();
render::ItemID _renderItemID;
bool _isLoaded;
float _alpha;
float _glowLevel;
@ -106,5 +120,11 @@ protected:
QScriptEngine* _scriptEngine;
};
namespace render {
template <> const ItemKey payloadGetKey(const Overlay::Pointer& overlay);
template <> const Item::Bound payloadGetBound(const Overlay::Pointer& overlay);
template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args);
}
#endif // hifi_Overlay_h

View file

@ -16,6 +16,7 @@
#include <Application.h>
#include <avatar/AvatarManager.h>
#include <LODManager.h>
#include <render/Scene.h>
#include "BillboardOverlay.h"
#include "Circle3DOverlay.h"
@ -31,6 +32,7 @@
#include "TextOverlay.h"
#include "Text3DOverlay.h"
Overlays::Overlays() : _nextOverlayID(1) {
}
@ -38,23 +40,18 @@ Overlays::~Overlays() {
{
QWriteLocker lock(&_lock);
foreach(Overlay* thisOverlay, _overlaysHUD) {
delete thisOverlay;
QWriteLocker deleteLock(&_deleteLock);
foreach(Overlay::Pointer overlay, _overlaysHUD) {
_overlaysToDelete.push_back(overlay);
}
foreach(Overlay::Pointer overlay, _overlaysWorld) {
_overlaysToDelete.push_back(overlay);
}
_overlaysHUD.clear();
foreach(Overlay* thisOverlay, _overlaysWorld) {
delete thisOverlay;
}
_overlaysWorld.clear();
}
if (!_overlaysToDelete.isEmpty()) {
QWriteLocker lock(&_deleteLock);
do {
delete _overlaysToDelete.takeLast();
} while (!_overlaysToDelete.isEmpty());
}
cleanupOverlaysToDelete();
}
void Overlays::init() {
@ -65,100 +62,61 @@ void Overlays::update(float deltatime) {
{
QWriteLocker lock(&_lock);
foreach(Overlay* thisOverlay, _overlaysHUD) {
foreach(Overlay::Pointer thisOverlay, _overlaysHUD) {
thisOverlay->update(deltatime);
}
foreach(Overlay* thisOverlay, _overlaysWorld) {
foreach(Overlay::Pointer thisOverlay, _overlaysWorld) {
thisOverlay->update(deltatime);
}
}
if (!_overlaysToDelete.isEmpty()) {
QWriteLocker lock(&_deleteLock);
do {
delete _overlaysToDelete.takeLast();
} while (!_overlaysToDelete.isEmpty());
}
cleanupOverlaysToDelete();
}
void Overlays::renderHUD() {
void Overlays::cleanupOverlaysToDelete() {
if (!_overlaysToDelete.isEmpty()) {
render::ScenePointer scene = Application::getInstance()->getMain3DScene();
render::PendingChanges pendingChanges;
{
QWriteLocker lock(&_deleteLock);
do {
Overlay::Pointer overlay = _overlaysToDelete.takeLast();
auto itemID = overlay->getRenderItemID();
if (itemID != render::Item::INVALID_ITEM_ID) {
overlay->removeFromScene(overlay, scene, pendingChanges);
}
} while (!_overlaysToDelete.isEmpty());
}
if (pendingChanges._removedItems.size() > 0) {
scene->enqueuePendingChanges(pendingChanges);
}
}
}
void Overlays::renderHUD(RenderArgs* renderArgs) {
QReadLocker lock(&_lock);
auto lodManager = DependencyManager::get<LODManager>();
RenderArgs args(NULL, Application::getInstance()->getViewFrustum(),
lodManager->getOctreeSizeScale(),
lodManager->getBoundaryLevelAdjust(),
RenderArgs::DEFAULT_RENDER_MODE, RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE);
foreach(Overlay* thisOverlay, _overlaysHUD) {
foreach(Overlay::Pointer thisOverlay, _overlaysHUD) {
if (thisOverlay->is3D()) {
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
thisOverlay->render(&args);
thisOverlay->render(renderArgs);
glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);
} else{
thisOverlay->render(&args);
} else {
thisOverlay->render(renderArgs);
}
}
}
void Overlays::renderWorld(bool drawFront,
RenderArgs::RenderMode renderMode,
RenderArgs::RenderSide renderSide,
RenderArgs::DebugFlags renderDebugFlags) {
QReadLocker lock(&_lock);
if (_overlaysWorld.size() == 0) {
return;
}
bool myAvatarComputed = false;
MyAvatar* avatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
glm::quat myAvatarRotation;
glm::vec3 myAvatarPosition(0.0f);
float angle = 0.0f;
glm::vec3 axis(0.0f, 1.0f, 0.0f);
float myAvatarScale = 1.0f;
auto lodManager = DependencyManager::get<LODManager>();
RenderArgs args(NULL, Application::getInstance()->getDisplayViewFrustum(),
lodManager->getOctreeSizeScale(),
lodManager->getBoundaryLevelAdjust(),
renderMode, renderSide, renderDebugFlags);
foreach(Overlay* thisOverlay, _overlaysWorld) {
Base3DOverlay* overlay3D = static_cast<Base3DOverlay*>(thisOverlay);
if (overlay3D->getDrawInFront() != drawFront) {
continue;
}
glPushMatrix();
switch (thisOverlay->getAnchor()) {
case Overlay::MY_AVATAR:
if (!myAvatarComputed) {
myAvatarRotation = avatar->getOrientation();
myAvatarPosition = avatar->getPosition();
angle = glm::degrees(glm::angle(myAvatarRotation));
axis = glm::axis(myAvatarRotation);
myAvatarScale = avatar->getScale();
myAvatarComputed = true;
}
glTranslatef(myAvatarPosition.x, myAvatarPosition.y, myAvatarPosition.z);
glRotatef(angle, axis.x, axis.y, axis.z);
glScalef(myAvatarScale, myAvatarScale, myAvatarScale);
break;
default:
break;
}
thisOverlay->render(&args);
glPopMatrix();
}
}
unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& properties) {
unsigned int thisID = 0;
Overlay* thisOverlay = NULL;
@ -201,6 +159,7 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope
}
unsigned int Overlays::addOverlay(Overlay* overlay) {
Overlay::Pointer overlayPointer(overlay);
overlay->init(_scriptEngine);
QWriteLocker lock(&_lock);
@ -209,19 +168,26 @@ unsigned int Overlays::addOverlay(Overlay* overlay) {
if (overlay->is3D()) {
Base3DOverlay* overlay3D = static_cast<Base3DOverlay*>(overlay);
if (overlay3D->getDrawOnHUD()) {
_overlaysHUD[thisID] = overlay;
_overlaysHUD[thisID] = overlayPointer;
} else {
_overlaysWorld[thisID] = overlay;
_overlaysWorld[thisID] = overlayPointer;
render::ScenePointer scene = Application::getInstance()->getMain3DScene();
render::PendingChanges pendingChanges;
overlayPointer->addToScene(overlayPointer, scene, pendingChanges);
scene->enqueuePendingChanges(pendingChanges);
}
} else {
_overlaysHUD[thisID] = overlay;
_overlaysHUD[thisID] = overlayPointer;
}
return thisID;
}
unsigned int Overlays::cloneOverlay(unsigned int id) {
Overlay* thisOverlay = NULL;
Overlay::Pointer thisOverlay = NULL;
if (_overlaysHUD.contains(id)) {
thisOverlay = _overlaysHUD[id];
} else if (_overlaysWorld.contains(id)) {
@ -237,7 +203,7 @@ unsigned int Overlays::cloneOverlay(unsigned int id) {
bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) {
QWriteLocker lock(&_lock);
Overlay* thisOverlay = NULL;
Overlay::Pointer thisOverlay;
if (_overlaysHUD.contains(id)) {
thisOverlay = _overlaysHUD[id];
@ -247,7 +213,7 @@ bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) {
if (thisOverlay) {
if (thisOverlay->is3D()) {
Base3DOverlay* overlay3D = static_cast<Base3DOverlay*>(thisOverlay);
Base3DOverlay* overlay3D = static_cast<Base3DOverlay*>(thisOverlay.get());
bool oldDrawOnHUD = overlay3D->getDrawOnHUD();
thisOverlay->setProperties(properties);
@ -272,7 +238,7 @@ bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) {
}
void Overlays::deleteOverlay(unsigned int id) {
Overlay* overlayToDelete;
Overlay::Pointer overlayToDelete;
{
QWriteLocker lock(&_lock);
@ -296,7 +262,7 @@ unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) {
}
QReadLocker lock(&_lock);
QMapIterator<unsigned int, Overlay*> i(_overlaysHUD);
QMapIterator<unsigned int, Overlay::Pointer> i(_overlaysHUD);
i.toBack();
const float LARGE_NEGATIVE_FLOAT = -9999999;
@ -309,14 +275,14 @@ unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) {
i.previous();
unsigned int thisID = i.key();
if (i.value()->is3D()) {
Base3DOverlay* thisOverlay = static_cast<Base3DOverlay*>(i.value());
Base3DOverlay* thisOverlay = static_cast<Base3DOverlay*>(i.value().get());
if (!thisOverlay->getIgnoreRayIntersection()) {
if (thisOverlay->findRayIntersection(origin, direction, distance, thisFace)) {
return thisID;
}
}
} else {
Overlay2D* thisOverlay = static_cast<Overlay2D*>(i.value());
Overlay2D* thisOverlay = static_cast<Overlay2D*>(i.value().get());
if (thisOverlay->getVisible() && thisOverlay->isLoaded() &&
thisOverlay->getBounds().contains(pointCopy.x, pointCopy.y, false)) {
return thisID;
@ -329,7 +295,7 @@ unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) {
OverlayPropertyResult Overlays::getProperty(unsigned int id, const QString& property) {
OverlayPropertyResult result;
Overlay* thisOverlay = NULL;
Overlay::Pointer thisOverlay;
QReadLocker lock(&_lock);
if (_overlaysHUD.contains(id)) {
thisOverlay = _overlaysHUD[id];
@ -376,12 +342,12 @@ RayToOverlayIntersectionResult Overlays::findRayIntersection(const PickRay& ray)
float bestDistance = std::numeric_limits<float>::max();
bool bestIsFront = false;
RayToOverlayIntersectionResult result;
QMapIterator<unsigned int, Overlay*> i(_overlaysWorld);
QMapIterator<unsigned int, Overlay::Pointer> i(_overlaysWorld);
i.toBack();
while (i.hasPrevious()) {
i.previous();
unsigned int thisID = i.key();
Base3DOverlay* thisOverlay = static_cast<Base3DOverlay*>(i.value());
Base3DOverlay* thisOverlay = static_cast<Base3DOverlay*>(i.value().get());
if (thisOverlay->getVisible() && !thisOverlay->getIgnoreRayIntersection() && thisOverlay->isLoaded()) {
float thisDistance;
BoxFace thisFace;
@ -483,7 +449,7 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R
bool Overlays::isLoaded(unsigned int id) {
QReadLocker lock(&_lock);
Overlay* thisOverlay = NULL;
Overlay::Pointer thisOverlay = NULL;
if (_overlaysHUD.contains(id)) {
thisOverlay = _overlaysHUD[id];
} else if (_overlaysWorld.contains(id)) {
@ -495,16 +461,16 @@ bool Overlays::isLoaded(unsigned int id) {
}
QSizeF Overlays::textSize(unsigned int id, const QString& text) const {
Overlay* thisOverlay = _overlaysHUD[id];
Overlay::Pointer thisOverlay = _overlaysHUD[id];
if (thisOverlay) {
if (typeid(*thisOverlay) == typeid(TextOverlay)) {
return static_cast<TextOverlay*>(thisOverlay)->textSize(text);
return static_cast<TextOverlay*>(thisOverlay.get())->textSize(text);
}
} else {
thisOverlay = _overlaysWorld[id];
if (thisOverlay) {
if (typeid(*thisOverlay) == typeid(Text3DOverlay)) {
return static_cast<Text3DOverlay*>(thisOverlay)->textSize(text);
return static_cast<Text3DOverlay*>(thisOverlay.get())->textSize(text);
}
}
}

View file

@ -53,10 +53,7 @@ public:
~Overlays();
void init();
void update(float deltatime);
void renderWorld(bool drawFront, RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE,
RenderArgs::RenderSide renderSide = RenderArgs::MONO,
RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE);
void renderHUD();
void renderHUD(RenderArgs* renderArgs);
public slots:
/// adds an overlay with the specific properties
@ -92,9 +89,10 @@ public slots:
QSizeF textSize(unsigned int id, const QString& text) const;
private:
QMap<unsigned int, Overlay*> _overlaysHUD;
QMap<unsigned int, Overlay*> _overlaysWorld;
QList<Overlay*> _overlaysToDelete;
void cleanupOverlaysToDelete();
QMap<unsigned int, Overlay::Pointer> _overlaysHUD;
QMap<unsigned int, Overlay::Pointer> _overlaysWorld;
QList<Overlay::Pointer> _overlaysToDelete;
unsigned int _nextOverlayID;
QReadWriteLock _lock;
QReadWriteLock _deleteLock;

View file

@ -0,0 +1,75 @@
//
// OverlaysPayload.cpp
// interface/src/ui/overlays
//
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QScriptValueIterator>
#include <limits>
#include <typeinfo>
#include <Application.h>
#include <avatar/AvatarManager.h>
#include <LODManager.h>
#include <render/Scene.h>
#include "BillboardOverlay.h"
#include "Circle3DOverlay.h"
#include "Cube3DOverlay.h"
#include "ImageOverlay.h"
#include "Line3DOverlay.h"
#include "LocalModelsOverlay.h"
#include "ModelOverlay.h"
#include "Overlays.h"
#include "Rectangle3DOverlay.h"
#include "Sphere3DOverlay.h"
#include "Grid3DOverlay.h"
#include "TextOverlay.h"
#include "Text3DOverlay.h"
namespace render {
template <> const ItemKey payloadGetKey(const Overlay::Pointer& overlay) {
if (overlay->is3D() && !static_cast<Base3DOverlay*>(overlay.get())->getDrawOnHUD()) {
if (static_cast<Base3DOverlay*>(overlay.get())->getDrawInFront()) {
return ItemKey::Builder().withTypeShape().withNoDepthSort().build();
} else {
return ItemKey::Builder::opaqueShape();
}
} else {
return ItemKey::Builder().withTypeShape().withViewSpace().build();
}
}
template <> const Item::Bound payloadGetBound(const Overlay::Pointer& overlay) {
if (overlay->is3D()) {
return static_cast<Base3DOverlay*>(overlay.get())->getBounds();
} else {
QRect bounds = static_cast<Overlay2D*>(overlay.get())->getBounds();
return AABox(glm::vec3(bounds.x(), bounds.y(), 0.0f), glm::vec3(bounds.width(), bounds.height(), 0.1f));
}
}
template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args) {
if (args) {
glPushMatrix();
if (overlay->getAnchor() == Overlay::MY_AVATAR) {
MyAvatar* avatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
glm::quat myAvatarRotation = avatar->getOrientation();
glm::vec3 myAvatarPosition = avatar->getPosition();
float angle = glm::degrees(glm::angle(myAvatarRotation));
glm::vec3 axis = glm::axis(myAvatarRotation);
float myAvatarScale = avatar->getScale();
glTranslatef(myAvatarPosition.x, myAvatarPosition.y, myAvatarPosition.z);
glRotatef(angle, axis.x, axis.y, axis.z);
glScalef(myAvatarScale, myAvatarScale, myAvatarScale);
}
overlay->render(args);
glPopMatrix();
}
}
}

View file

@ -11,15 +11,12 @@
// include this before QGLWidget, which includes an earlier version of OpenGL
#include "InterfaceConfig.h"
#include <TextRenderer.h>
#include "Application.h"
#include "Text3DOverlay.h"
const xColor DEFAULT_BACKGROUND_COLOR = { 0, 0, 0 };
const float DEFAULT_BACKGROUND_ALPHA = 0.7f;
const float DEFAULT_MARGIN = 0.1f;
const int FIXED_FONT_POINT_SIZE = 40;
const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 40.0f; // this is a ratio determined through experimentation
const float LINE_SCALE_RATIO = 1.2f;
@ -50,6 +47,7 @@ Text3DOverlay::Text3DOverlay(const Text3DOverlay* text3DOverlay) :
}
Text3DOverlay::~Text3DOverlay() {
delete _textRenderer;
}
xColor Text3DOverlay::getBackgroundColor() {
@ -106,8 +104,7 @@ void Text3DOverlay::render(RenderArgs* args) {
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, quadColor);
// Same font properties as textSize()
TextRenderer* textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE);
float maxHeight = (float)textRenderer->computeExtent("Xy").y * LINE_SCALE_RATIO;
float maxHeight = (float)_textRenderer->computeExtent("Xy").y * LINE_SCALE_RATIO;
float scaleFactor = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight;
@ -124,7 +121,7 @@ void Text3DOverlay::render(RenderArgs* args) {
enableClipPlane(GL_CLIP_PLANE3, 0.0f, 1.0f, 0.0f, -clipMinimum.y);
glm::vec4 textColor = { _color.red / MAX_COLOR, _color.green / MAX_COLOR, _color.blue / MAX_COLOR, getAlpha() };
textRenderer->draw(0, 0, _text, textColor);
_textRenderer->draw(0, 0, _text, textColor);
glDisable(GL_CLIP_PLANE0);
glDisable(GL_CLIP_PLANE1);
@ -228,10 +225,9 @@ Text3DOverlay* Text3DOverlay::createClone() const {
}
QSizeF Text3DOverlay::textSize(const QString& text) const {
auto textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE);
auto extents = textRenderer->computeExtent(text);
auto extents = _textRenderer->computeExtent(text);
float maxHeight = (float)textRenderer->computeExtent("Xy").y * LINE_SCALE_RATIO;
float maxHeight = (float)_textRenderer->computeExtent("Xy").y * LINE_SCALE_RATIO;
float pointToWorldScale = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight;
return QSizeF(extents.x, extents.y) * pointToWorldScale;

View file

@ -17,8 +17,12 @@
#include <QString>
#include <RenderArgs.h>
#include <TextRenderer.h>
#include "Planar3DOverlay.h"
const int FIXED_FONT_POINT_SIZE = 40;
class Text3DOverlay : public Planar3DOverlay {
Q_OBJECT
@ -58,6 +62,8 @@ public:
private:
void enableClipPlane(GLenum plane, float x, float y, float z, float w);
TextRenderer* _textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE);
QString _text;
xColor _backgroundColor;
float _backgroundAlpha;

View file

@ -14,7 +14,6 @@
#include <DependencyManager.h>
#include <GeometryCache.h>
#include <SharedUtil.h>
#include <TextRenderer.h>
#include "TextOverlay.h"
@ -25,6 +24,7 @@ TextOverlay::TextOverlay() :
_topMargin(DEFAULT_MARGIN),
_fontSize(DEFAULT_FONTSIZE)
{
_textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, _fontSize, DEFAULT_FONT_WEIGHT);
}
TextOverlay::TextOverlay(const TextOverlay* textOverlay) :
@ -36,9 +36,11 @@ TextOverlay::TextOverlay(const TextOverlay* textOverlay) :
_topMargin(textOverlay->_topMargin),
_fontSize(textOverlay->_fontSize)
{
_textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, _fontSize, DEFAULT_FONT_WEIGHT);
}
TextOverlay::~TextOverlay() {
delete _textRenderer;
}
xColor TextOverlay::getBackgroundColor() {
@ -79,9 +81,6 @@ void TextOverlay::render(RenderArgs* args) {
glm::vec2 topLeft(left, top);
glm::vec2 bottomRight(right, bottom);
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, quadColor);
// Same font properties as textSize()
TextRenderer* textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, _fontSize, DEFAULT_FONT_WEIGHT);
const int leftAdjust = -1; // required to make text render relative to left edge of bounds
const int topAdjust = -2; // required to make text render relative to top edge of bounds
@ -90,7 +89,7 @@ void TextOverlay::render(RenderArgs* args) {
float alpha = getAlpha();
glm::vec4 textColor = {_color.red / MAX_COLOR, _color.green / MAX_COLOR, _color.blue / MAX_COLOR, alpha };
textRenderer->draw(x, y, _text, textColor);
_textRenderer->draw(x, y, _text, textColor);
}
void TextOverlay::setProperties(const QScriptValue& properties) {
@ -163,8 +162,7 @@ QScriptValue TextOverlay::getProperty(const QString& property) {
}
QSizeF TextOverlay::textSize(const QString& text) const {
auto textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, _fontSize, DEFAULT_FONT_WEIGHT);
auto extents = textRenderer->computeExtent(text);
auto extents = _textRenderer->computeExtent(text);
return QSizeF(extents.x, extents.y);
}

View file

@ -19,6 +19,7 @@
#include <QString>
#include <SharedUtil.h>
#include <TextRenderer.h>
#include "Overlay.h"
#include "Overlay2D.h"
@ -58,6 +59,9 @@ public:
QSizeF textSize(const QString& text) const; // Pixels
private:
TextRenderer* _textRenderer = nullptr;
QString _text;
xColor _backgroundColor;
float _backgroundAlpha;

View file

@ -153,6 +153,7 @@ void AudioClient::reset() {
}
void AudioClient::audioMixerKilled() {
_hasReceivedFirstPacket = false;
_outgoingAvatarAudioSequenceNumber = 0;
_stats.reset();
}
@ -481,6 +482,7 @@ void AudioClient::start() {
qCDebug(audioclient) << "Unable to set up audio input because of a problem with input format.";
qCDebug(audioclient) << "The closest format available is" << inputDeviceInfo.nearestFormat(_desiredInputFormat);
}
if (!outputFormatSupported) {
qCDebug(audioclient) << "Unable to set up audio output because of a problem with output format.";
qCDebug(audioclient) << "The closest format available is" << outputDeviceInfo.nearestFormat(_desiredOutputFormat);
@ -489,6 +491,7 @@ void AudioClient::start() {
if (_audioInput) {
_inputFrameBuffer.initialize( _inputFormat.channelCount(), _audioInput->bufferSize() * 8 );
}
_inputGain.initialize();
_sourceGain.initialize();
_noiseSource.initialize();
@ -926,6 +929,14 @@ void AudioClient::addReceivedAudioToStream(const QByteArray& audioByteArray) {
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveFirstAudioPacket);
if (_audioOutput) {
if (!_hasReceivedFirstPacket) {
_hasReceivedFirstPacket = true;
// have the audio scripting interface emit a signal to say we just connected to mixer
emit receivedFirstPacket();
}
// Audio output must exist and be correctly set up if we're going to process received audio
_receivedAudioStream.parseData(audioByteArray);
}

View file

@ -37,6 +37,7 @@
#include <MixedProcessedAudioStream.h>
#include <RingBufferHistory.h>
#include <SettingHandle.h>
#include <Sound.h>
#include <StDev.h>
#include "AudioIOStats.h"
@ -57,7 +58,7 @@ static const int NUM_AUDIO_CHANNELS = 2;
static const int DEFAULT_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES = 3;
static const int MIN_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES = 1;
static const int MAX_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES = 20;
#if defined(Q_OS_ANDROID) || defined(Q_OS_WIN)
#if defined(Q_OS_ANDROID) || defined(Q_OS_WIN)
static const int DEFAULT_AUDIO_OUTPUT_STARVE_DETECTION_ENABLED = false;
#else
static const int DEFAULT_AUDIO_OUTPUT_STARVE_DETECTION_ENABLED = true;
@ -88,14 +89,14 @@ public:
void stop() { close(); }
qint64 readData(char * data, qint64 maxSize);
qint64 writeData(const char * data, qint64 maxSize) { return 0; }
int getRecentUnfulfilledReads() { int unfulfilledReads = _unfulfilledReads; _unfulfilledReads = 0; return unfulfilledReads; }
private:
MixedProcessedAudioStream& _receivedAudioStream;
AudioClient* _audio;
int _unfulfilledReads;
};
const MixedProcessedAudioStream& getReceivedAudioStream() const { return _receivedAudioStream; }
MixedProcessedAudioStream& getReceivedAudioStream() { return _receivedAudioStream; }
@ -105,30 +106,30 @@ public:
float getAudioAverageInputLoudness() const { return _lastInputLoudness; }
int getDesiredJitterBufferFrames() const { return _receivedAudioStream.getDesiredJitterBufferFrames(); }
bool isMuted() { return _muted; }
const AudioIOStats& getStats() const { return _stats; }
float getInputRingBufferMsecsAvailable() const;
float getAudioOutputMsecsUnplayed() const;
int getOutputBufferSize() { return _outputBufferSizeFrames.get(); }
bool getOutputStarveDetectionEnabled() { return _outputStarveDetectionEnabled.get(); }
void setOutputStarveDetectionEnabled(bool enabled) { _outputStarveDetectionEnabled.set(enabled); }
int getOutputStarveDetectionPeriod() { return _outputStarveDetectionPeriodMsec.get(); }
void setOutputStarveDetectionPeriod(int msecs) { _outputStarveDetectionPeriodMsec.set(msecs); }
int getOutputStarveDetectionThreshold() { return _outputStarveDetectionThreshold.get(); }
void setOutputStarveDetectionThreshold(int threshold) { _outputStarveDetectionThreshold.set(threshold); }
void setPositionGetter(AudioPositionGetter positionGetter) { _positionGetter = positionGetter; }
void setOrientationGetter(AudioOrientationGetter orientationGetter) { _orientationGetter = orientationGetter; }
static const float CALLBACK_ACCELERATOR_RATIO;
public slots:
void start();
void stop();
@ -140,7 +141,7 @@ public slots:
void reset();
void audioMixerKilled();
void toggleMute();
virtual void enableAudioSourceInject(bool enable);
virtual void selectAudioSourcePinkNoise();
virtual void selectAudioSourceSine440();
@ -148,10 +149,10 @@ public slots:
virtual void setIsStereoInput(bool stereo);
void toggleAudioNoiseReduction() { _isNoiseGateEnabled = !_isNoiseGateEnabled; }
void toggleLocalEcho() { _shouldEchoLocally = !_shouldEchoLocally; }
void toggleServerEcho() { _shouldEchoToServer = !_shouldEchoToServer; }
void processReceivedSamples(const QByteArray& inputBuffer, QByteArray& outputBuffer);
void sendMuteEnvironmentPacket();
@ -172,10 +173,10 @@ public slots:
void setReverbOptions(const AudioEffectOptions* options);
void outputNotify();
void loadSettings();
void saveSettings();
signals:
bool muteToggled();
void inputReceived(const QByteArray& inputSamples);
@ -184,14 +185,16 @@ signals:
void deviceChanged();
void receivedFirstPacket();
protected:
AudioClient();
~AudioClient();
virtual void customDeleter() {
deleteLater();
}
private:
void outputFormatChanged();
@ -216,35 +219,35 @@ private:
QString _inputAudioDeviceName;
QString _outputAudioDeviceName;
quint64 _outputStarveDetectionStartTimeMsec;
int _outputStarveDetectionCount;
Setting::Handle<int> _outputBufferSizeFrames;
Setting::Handle<bool> _outputStarveDetectionEnabled;
Setting::Handle<int> _outputStarveDetectionPeriodMsec;
// Maximum number of starves per _outputStarveDetectionPeriod before increasing buffer size
Setting::Handle<int> _outputStarveDetectionThreshold;
StDev _stdev;
QElapsedTimer _timeSinceLastReceived;
float _averagedLatency;
float _lastInputLoudness;
float _timeSinceLastClip;
int _totalInputAudioSamples;
bool _muted;
bool _shouldEchoLocally;
bool _shouldEchoToServer;
bool _isNoiseGateEnabled;
bool _audioSourceInjectEnabled;
bool _reverb;
AudioEffectOptions _scriptReverbOptions;
AudioEffectOptions _zoneReverbOptions;
AudioEffectOptions* _reverbOptions;
ty_gverb* _gverb;
// possible soxr streams needed for resample
soxr* _inputToNetworkResampler;
soxr* _networkToOutputResampler;
@ -268,17 +271,17 @@ private:
// Input framebuffer
AudioBufferFloat32 _inputFrameBuffer;
// Input gain
AudioGain _inputGain;
// Post tone/pink noise generator gain
AudioGain _sourceGain;
// Pink noise source
bool _noiseSourceEnabled;
AudioSourcePinkNoise _noiseSource;
// Tone source
bool _toneSourceEnabled;
AudioSourceTone _toneSource;
@ -286,17 +289,19 @@ private:
quint16 _outgoingAvatarAudioSequenceNumber;
AudioOutputIODevice _audioOutputIODevice;
AudioIOStats _stats;
AudioNoiseGate _inputGate;
AudioPositionGetter _positionGetter;
AudioOrientationGetter _orientationGetter;
QVector<QString> _inputDevices;
QVector<QString> _outputDevices;
void checkDevices();
bool _hasReceivedFirstPacket = false;
};

View file

@ -26,7 +26,7 @@
AudioInjector::AudioInjector(QObject* parent) :
QObject(parent)
{
}
AudioInjector::AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions) :
@ -39,24 +39,25 @@ AudioInjector::AudioInjector(const QByteArray& audioData, const AudioInjectorOpt
_audioData(audioData),
_options(injectorOptions)
{
}
void AudioInjector::setIsFinished(bool isFinished) {
_isFinished = isFinished;
// In all paths, regardless of isFinished argument. restart() passes false to prepare for new play, and injectToMixer() needs _shouldStop reset.
_shouldStop = false;
if (_isFinished) {
emit finished();
if (_localBuffer) {
_localBuffer->stop();
_localBuffer->deleteLater();
_localBuffer = NULL;
}
_isStarted = false;
_shouldStop = false;
if (_shouldDeleteAfterFinish) {
// we've been asked to delete after finishing, trigger a queued deleteLater here
qCDebug(audio) << "AudioInjector triggering delete from setIsFinished";
@ -67,18 +68,19 @@ void AudioInjector::setIsFinished(bool isFinished) {
void AudioInjector::injectAudio() {
if (!_isStarted) {
_isStarted = true;
// check if we need to offset the sound by some number of seconds
if (_options.secondOffset > 0.0f) {
// convert the offset into a number of bytes
int byteOffset = (int) floorf(AudioConstants::SAMPLE_RATE * _options.secondOffset * (_options.stereo ? 2.0f : 1.0f));
byteOffset *= sizeof(int16_t);
_currentSendPosition = byteOffset;
} else {
_currentSendPosition = 0;
}
if (_options.localOnly) {
injectLocally();
} else {
@ -86,12 +88,20 @@ void AudioInjector::injectAudio() {
}
} else {
qCDebug(audio) << "AudioInjector::injectAudio called but already started.";
}
}
}
void AudioInjector::restart() {
qCDebug(audio) << "Restarting an AudioInjector by stopping and starting over.";
stop();
connect(this, &AudioInjector::finished, this, &AudioInjector::restartPortionAfterFinished);
if (!_isStarted || _isFinished) {
emit finished();
} else {
stop();
}
}
void AudioInjector::restartPortionAfterFinished() {
disconnect(this, &AudioInjector::finished, this, &AudioInjector::restartPortionAfterFinished);
setIsFinished(false);
QMetaObject::invokeMethod(this, "injectAudio", Qt::QueuedConnection);
}
@ -100,37 +110,37 @@ void AudioInjector::injectLocally() {
bool success = false;
if (_localAudioInterface) {
if (_audioData.size() > 0) {
_localBuffer = new AudioInjectorLocalBuffer(_audioData, this);
_localBuffer->open(QIODevice::ReadOnly);
_localBuffer->setShouldLoop(_options.loop);
_localBuffer->setVolume(_options.volume);
// give our current send position to the local buffer
_localBuffer->setCurrentOffset(_currentSendPosition);
success = _localAudioInterface->outputLocalInjector(_options.stereo, this);
// if we're not looping and the buffer tells us it is empty then emit finished
connect(_localBuffer, &AudioInjectorLocalBuffer::bufferEmpty, this, &AudioInjector::stop);
if (!success) {
qCDebug(audio) << "AudioInjector::injectLocally could not output locally via _localAudioInterface";
}
} else {
qCDebug(audio) << "AudioInjector::injectLocally called without any data in Sound QByteArray";
}
} else {
qCDebug(audio) << "AudioInjector::injectLocally cannot inject locally with no local audio interface present.";
}
if (!success) {
// we never started so we are finished, call our stop method
stop();
}
}
const uchar MAX_INJECTOR_VOLUME = 0xFF;
@ -140,65 +150,65 @@ void AudioInjector::injectToMixer() {
_currentSendPosition >= _audioData.size()) {
_currentSendPosition = 0;
}
auto nodeList = DependencyManager::get<NodeList>();
// make sure we actually have samples downloaded to inject
if (_audioData.size()) {
// setup the packet for injected audio
QByteArray injectAudioPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeInjectAudio);
QDataStream packetStream(&injectAudioPacket, QIODevice::Append);
// pack some placeholder sequence number for now
int numPreSequenceNumberBytes = injectAudioPacket.size();
packetStream << (quint16)0;
// pack stream identifier (a generated UUID)
packetStream << QUuid::createUuid();
// pack the stereo/mono type of the stream
packetStream << _options.stereo;
// pack the flag for loopback
uchar loopbackFlag = (uchar) true;
packetStream << loopbackFlag;
// pack the position for injected audio
int positionOptionOffset = injectAudioPacket.size();
packetStream.writeRawData(reinterpret_cast<const char*>(&_options.position),
sizeof(_options.position));
// pack our orientation for injected audio
int orientationOptionOffset = injectAudioPacket.size();
packetStream.writeRawData(reinterpret_cast<const char*>(&_options.orientation),
sizeof(_options.orientation));
// pack zero for radius
float radius = 0;
packetStream << radius;
// pack 255 for attenuation byte
int volumeOptionOffset = injectAudioPacket.size();
quint8 volume = MAX_INJECTOR_VOLUME * _options.volume;
packetStream << volume;
packetStream << _options.ignorePenumbra;
QElapsedTimer timer;
timer.start();
int nextFrame = 0;
int numPreAudioDataBytes = injectAudioPacket.size();
bool shouldLoop = _options.loop;
// loop to send off our audio in NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL byte chunks
quint16 outgoingInjectedAudioSequenceNumber = 0;
while (_currentSendPosition < _audioData.size() && !_shouldStop) {
int bytesToCopy = std::min(((_options.stereo) ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL,
_audioData.size() - _currentSendPosition);
// Measure the loudness of this frame
_loudness = 0.0f;
for (int i = 0; i < bytesToCopy; i += sizeof(int16_t)) {
@ -215,45 +225,45 @@ void AudioInjector::injectToMixer() {
sizeof(_options.orientation));
volume = MAX_INJECTOR_VOLUME * _options.volume;
memcpy(injectAudioPacket.data() + volumeOptionOffset, &volume, sizeof(volume));
// resize the QByteArray to the right size
injectAudioPacket.resize(numPreAudioDataBytes + bytesToCopy);
// pack the sequence number
memcpy(injectAudioPacket.data() + numPreSequenceNumberBytes,
&outgoingInjectedAudioSequenceNumber, sizeof(quint16));
// copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet
memcpy(injectAudioPacket.data() + numPreAudioDataBytes,
_audioData.data() + _currentSendPosition, bytesToCopy);
// grab our audio mixer from the NodeList, if it exists
SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer);
// send off this audio packet
nodeList->writeDatagram(injectAudioPacket, audioMixer);
outgoingInjectedAudioSequenceNumber++;
_currentSendPosition += bytesToCopy;
// send two packets before the first sleep so the mixer can start playback right away
if (_currentSendPosition != bytesToCopy && _currentSendPosition < _audioData.size()) {
// process events in case we have been told to stop and be deleted
QCoreApplication::processEvents();
if (_shouldStop) {
break;
}
// not the first packet and not done
// sleep for the appropriate time
int usecToSleep = (++nextFrame * AudioConstants::NETWORK_FRAME_USECS) - timer.nsecsElapsed() / 1000;
if (usecToSleep > 0) {
usleep(usecToSleep);
}
}
}
if (shouldLoop && _currentSendPosition >= _audioData.size()) {
@ -261,13 +271,13 @@ void AudioInjector::injectToMixer() {
}
}
}
setIsFinished(true);
}
void AudioInjector::stop() {
_shouldStop = true;
if (_options.localOnly) {
// we're only a local injector, so we can say we are finished right away too
setIsFinished(true);

View file

@ -59,6 +59,7 @@ public slots:
void setCurrentSendPosition(int currentSendPosition) { _currentSendPosition = currentSendPosition; }
float getLoudness() const { return _loudness; }
bool isPlaying() const { return !_isFinished; }
void restartPortionAfterFinished();
signals:
void finished();

View file

@ -53,21 +53,25 @@ Sound::Sound(const QUrl& url, bool isStereo) :
_isStereo(isStereo),
_isReady(false)
{
}
void Sound::downloadFinished(QNetworkReply* reply) {
// replace our byte array with the downloaded data
QByteArray rawAudioByteArray = reply->readAll();
QString fileName = reply->url().fileName();
if (reply->hasRawHeader("Content-Type")) {
const QString WAV_EXTENSION = ".wav";
if (reply->hasRawHeader("Content-Type") || fileName.endsWith(WAV_EXTENSION)) {
QByteArray headerContentType = reply->rawHeader("Content-Type");
// WAV audio file encountered
if (headerContentType == "audio/x-wav"
|| headerContentType == "audio/wav"
|| headerContentType == "audio/wave") {
|| headerContentType == "audio/wave"
|| fileName.endsWith(WAV_EXTENSION)) {
QByteArray outputAudioByteArray;
@ -80,7 +84,7 @@ void Sound::downloadFinished(QNetworkReply* reply) {
_isStereo = true;
qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes from" << reply->url() << "as stereo audio file.";
}
// Process as RAW file
downSample(rawAudioByteArray);
}
@ -88,7 +92,7 @@ void Sound::downloadFinished(QNetworkReply* reply) {
} else {
qCDebug(audio) << "Network reply without 'Content-Type'.";
}
_isReady = true;
reply->deleteLater();
}
@ -99,16 +103,16 @@ void Sound::downSample(const QByteArray& rawAudioByteArray) {
// we want to convert it to the format that the audio-mixer wants
// which is signed, 16-bit, 24Khz
int numSourceSamples = rawAudioByteArray.size() / sizeof(AudioConstants::AudioSample);
int numDestinationBytes = rawAudioByteArray.size() / sizeof(AudioConstants::AudioSample);
if (_isStereo && numSourceSamples % 2 != 0) {
numDestinationBytes += sizeof(AudioConstants::AudioSample);
}
_byteArray.resize(numDestinationBytes);
int16_t* sourceSamples = (int16_t*) rawAudioByteArray.data();
int16_t* destinationSamples = (int16_t*) _byteArray.data();
@ -134,22 +138,22 @@ void Sound::downSample(const QByteArray& rawAudioByteArray) {
}
void Sound::trimFrames() {
const uint32_t inputFrameCount = _byteArray.size() / sizeof(int16_t);
const uint32_t trimCount = 1024; // number of leading and trailing frames to trim
if (inputFrameCount <= (2 * trimCount)) {
return;
}
int16_t* inputFrameData = (int16_t*)_byteArray.data();
AudioEditBufferFloat32 editBuffer(1, inputFrameCount);
editBuffer.copyFrames(1, inputFrameCount, inputFrameData, false /*copy in*/);
editBuffer.linearFade(0, trimCount, true);
editBuffer.linearFade(inputFrameCount - trimCount, inputFrameCount, false);
editBuffer.copyFrames(1, inputFrameCount, inputFrameData, true /*copy out*/);
}
@ -238,7 +242,7 @@ void Sound::interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& ou
} else if (qFromLittleEndian<quint16>(fileHeader.wave.numChannels) > 2) {
qCDebug(audio) << "Currently not support audio files with more than 2 channels.";
}
if (qFromLittleEndian<quint16>(fileHeader.wave.bitsPerSample) != 16) {
qCDebug(audio) << "Currently not supporting non 16bit audio files.";
return;

View file

@ -13,6 +13,7 @@
#define hifi_AvatarData_h
#include <string>
#include <memory>
/* VS2010 defines stdint.h, but not inttypes.h */
#if defined(_MSC_VER)
typedef signed char int8_t;
@ -57,6 +58,10 @@ typedef unsigned long long quint64;
#include "Recorder.h"
#include "Referential.h"
typedef std::shared_ptr<AvatarData> AvatarSharedPointer;
typedef std::weak_ptr<AvatarData> AvatarWeakPointer;
typedef QHash<QUuid, AvatarSharedPointer> AvatarHash;
// avatar motion behaviors
const quint32 AVATAR_MOTION_KEYBOARD_MOTOR_ENABLED = 1U << 0;
const quint32 AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED = 1U << 1;

View file

@ -16,16 +16,14 @@
#include <QtCore/QSharedPointer>
#include <QtCore/QUuid>
#include <memory>
#include <DependencyManager.h>
#include <Node.h>
#include "AvatarData.h"
#include <glm/glm.hpp>
typedef QSharedPointer<AvatarData> AvatarSharedPointer;
typedef QWeakPointer<AvatarData> AvatarWeakPointer;
typedef QHash<QUuid, AvatarSharedPointer> AvatarHash;
class AvatarHashMap : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY

View file

@ -23,4 +23,4 @@ find_package(Soxr REQUIRED)
target_link_libraries(${TARGET_NAME} ${SOXR_LIBRARIES})
target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${SOXR_INCLUDE_DIRS})
link_hifi_libraries(shared gpu script-engine render-utils)
link_hifi_libraries(shared gpu script-engine render render-utils)

View file

@ -10,6 +10,7 @@
//
#include <gpu/GPUConfig.h>
#include <gpu/GLBackend.h>
#include <glm/gtx/quaternion.hpp>
@ -30,8 +31,11 @@
#include <soxr.h>
#include <AudioConstants.h>
#include "EntityTreeRenderer.h"
#include "RenderableEntityItem.h"
#include "RenderableBoxEntityItem.h"
#include "RenderableLightEntityItem.h"
#include "RenderableModelEntityItem.h"
@ -95,6 +99,14 @@ void EntityTreeRenderer::clear() {
}
OctreeRenderer::clear();
_entityScripts.clear();
auto scene = _viewState->getMain3DScene();
render::PendingChanges pendingChanges;
foreach(auto entity, _entitiesInScene) {
entity->removeFromScene(entity, scene, pendingChanges);
}
scene->enqueuePendingChanges(pendingChanges);
_entitiesInScene.clear();
}
void EntityTreeRenderer::init() {
@ -395,127 +407,107 @@ void EntityTreeRenderer::leaveAllEntities() {
_lastAvatarPosition = _viewState->getAvatarPosition() + glm::vec3((float)TREE_SCALE);
}
}
void EntityTreeRenderer::render(RenderArgs::RenderMode renderMode,
RenderArgs::RenderSide renderSide,
RenderArgs::DebugFlags renderDebugFlags) {
void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr<ZoneEntityItem> zone) {
QSharedPointer<SceneScriptingInterface> scene = DependencyManager::get<SceneScriptingInterface>();
if (zone) {
if (!_hasPreviousZone) {
_previousKeyLightColor = scene->getKeyLightColor();
_previousKeyLightIntensity = scene->getKeyLightIntensity();
_previousKeyLightAmbientIntensity = scene->getKeyLightAmbientIntensity();
_previousKeyLightDirection = scene->getKeyLightDirection();
_previousStageSunModelEnabled = scene->isStageSunModelEnabled();
_previousStageLongitude = scene->getStageLocationLongitude();
_previousStageLatitude = scene->getStageLocationLatitude();
_previousStageAltitude = scene->getStageLocationAltitude();
_previousStageHour = scene->getStageDayTime();
_previousStageDay = scene->getStageYearTime();
_hasPreviousZone = true;
}
scene->setKeyLightColor(zone->getKeyLightColorVec3());
scene->setKeyLightIntensity(zone->getKeyLightIntensity());
scene->setKeyLightAmbientIntensity(zone->getKeyLightAmbientIntensity());
scene->setKeyLightDirection(zone->getKeyLightDirection());
scene->setStageSunModelEnable(zone->getStageProperties().getSunModelEnabled());
scene->setStageLocation(zone->getStageProperties().getLongitude(), zone->getStageProperties().getLatitude(),
zone->getStageProperties().getAltitude());
scene->setStageDayTime(zone->getStageProperties().calculateHour());
scene->setStageYearTime(zone->getStageProperties().calculateDay());
if (zone->getBackgroundMode() == BACKGROUND_MODE_ATMOSPHERE) {
EnvironmentData data = zone->getEnvironmentData();
glm::vec3 keyLightDirection = scene->getKeyLightDirection();
glm::vec3 inverseKeyLightDirection = keyLightDirection * -1.0f;
// NOTE: is this right? It seems like the "sun" should be based on the center of the
// atmosphere, not where the camera is.
glm::vec3 keyLightLocation = _viewState->getAvatarPosition()
+ (inverseKeyLightDirection * data.getAtmosphereOuterRadius());
data.setSunLocation(keyLightLocation);
const float KEY_LIGHT_INTENSITY_TO_SUN_BRIGHTNESS_RATIO = 20.0f;
float sunBrightness = scene->getKeyLightIntensity() * KEY_LIGHT_INTENSITY_TO_SUN_BRIGHTNESS_RATIO;
data.setSunBrightness(sunBrightness);
_viewState->overrideEnvironmentData(data);
scene->getSkyStage()->setBackgroundMode(model::SunSkyStage::SKY_DOME);
} else {
_viewState->endOverrideEnvironmentData();
auto stage = scene->getSkyStage();
if (zone->getBackgroundMode() == BACKGROUND_MODE_SKYBOX) {
stage->getSkybox()->setColor(zone->getSkyboxProperties().getColorVec3());
if (zone->getSkyboxProperties().getURL().isEmpty()) {
stage->getSkybox()->setCubemap(gpu::TexturePointer());
} else {
// Update the Texture of the Skybox with the one pointed by this zone
auto cubeMap = DependencyManager::get<TextureCache>()->getTexture(zone->getSkyboxProperties().getURL(), CUBE_TEXTURE);
stage->getSkybox()->setCubemap(cubeMap->getGPUTexture());
}
stage->setBackgroundMode(model::SunSkyStage::SKY_BOX);
} else {
stage->setBackgroundMode(model::SunSkyStage::SKY_DOME); // let the application atmosphere through
}
}
} else {
if (_hasPreviousZone) {
scene->setKeyLightColor(_previousKeyLightColor);
scene->setKeyLightIntensity(_previousKeyLightIntensity);
scene->setKeyLightAmbientIntensity(_previousKeyLightAmbientIntensity);
scene->setKeyLightDirection(_previousKeyLightDirection);
scene->setStageSunModelEnable(_previousStageSunModelEnabled);
scene->setStageLocation(_previousStageLongitude, _previousStageLatitude,
_previousStageAltitude);
scene->setStageDayTime(_previousStageHour);
scene->setStageYearTime(_previousStageDay);
_hasPreviousZone = false;
}
_viewState->endOverrideEnvironmentData();
scene->getSkyStage()->setBackgroundMode(model::SunSkyStage::SKY_DOME); // let the application atmosphere through
}
}
void EntityTreeRenderer::render(RenderArgs* renderArgs) {
if (_tree && !_shuttingDown) {
Model::startScene(renderSide);
ViewFrustum* frustum = (renderMode == RenderArgs::SHADOW_RENDER_MODE) ?
_viewState->getShadowViewFrustum() : _viewState->getCurrentViewFrustum();
RenderArgs args(this, frustum, getSizeScale(), getBoundaryLevelAdjust(),
renderMode, renderSide, renderDebugFlags);
renderArgs->_renderer = this;
_tree->lockForRead();
// Whenever you're in an intersection between zones, we will always choose the smallest zone.
_bestZone = NULL;
_bestZone = NULL; // NOTE: Is this what we want?
_bestZoneVolume = std::numeric_limits<float>::max();
_tree->recurseTreeWithOperation(renderOperation, &args);
QSharedPointer<SceneScriptingInterface> scene = DependencyManager::get<SceneScriptingInterface>();
if (_bestZone) {
if (!_hasPreviousZone) {
_previousKeyLightColor = scene->getKeyLightColor();
_previousKeyLightIntensity = scene->getKeyLightIntensity();
_previousKeyLightAmbientIntensity = scene->getKeyLightAmbientIntensity();
_previousKeyLightDirection = scene->getKeyLightDirection();
_previousStageSunModelEnabled = scene->isStageSunModelEnabled();
_previousStageLongitude = scene->getStageLocationLongitude();
_previousStageLatitude = scene->getStageLocationLatitude();
_previousStageAltitude = scene->getStageLocationAltitude();
_previousStageHour = scene->getStageDayTime();
_previousStageDay = scene->getStageYearTime();
_hasPreviousZone = true;
}
scene->setKeyLightColor(_bestZone->getKeyLightColorVec3());
scene->setKeyLightIntensity(_bestZone->getKeyLightIntensity());
scene->setKeyLightAmbientIntensity(_bestZone->getKeyLightAmbientIntensity());
scene->setKeyLightDirection(_bestZone->getKeyLightDirection());
scene->setStageSunModelEnable(_bestZone->getStageProperties().getSunModelEnabled());
scene->setStageLocation(_bestZone->getStageProperties().getLongitude(), _bestZone->getStageProperties().getLatitude(),
_bestZone->getStageProperties().getAltitude());
scene->setStageDayTime(_bestZone->getStageProperties().calculateHour());
scene->setStageYearTime(_bestZone->getStageProperties().calculateDay());
// FIX ME: right now the renderOperation does the following:
// 1) determining the best zone (not really rendering)
// 2) render the debug cell details
// we should clean this up
_tree->recurseTreeWithOperation(renderOperation, renderArgs);
if (_bestZone->getBackgroundMode() == BACKGROUND_MODE_ATMOSPHERE) {
EnvironmentData data = _bestZone->getEnvironmentData();
glm::vec3 keyLightDirection = scene->getKeyLightDirection();
glm::vec3 inverseKeyLightDirection = keyLightDirection * -1.0f;
// NOTE: is this right? It seems like the "sun" should be based on the center of the
// atmosphere, not where the camera is.
glm::vec3 keyLightLocation = _viewState->getAvatarPosition()
+ (inverseKeyLightDirection * data.getAtmosphereOuterRadius());
data.setSunLocation(keyLightLocation);
applyZonePropertiesToScene(_bestZone);
const float KEY_LIGHT_INTENSITY_TO_SUN_BRIGHTNESS_RATIO = 20.0f;
float sunBrightness = scene->getKeyLightIntensity() * KEY_LIGHT_INTENSITY_TO_SUN_BRIGHTNESS_RATIO;
data.setSunBrightness(sunBrightness);
_viewState->overrideEnvironmentData(data);
scene->getSkyStage()->setBackgroundMode(model::SunSkyStage::SKY_DOME);
} else {
_viewState->endOverrideEnvironmentData();
auto stage = scene->getSkyStage();
if (_bestZone->getBackgroundMode() == BACKGROUND_MODE_SKYBOX) {
stage->getSkybox()->setColor(_bestZone->getSkyboxProperties().getColorVec3());
if (_bestZone->getSkyboxProperties().getURL().isEmpty()) {
stage->getSkybox()->setCubemap(gpu::TexturePointer());
} else {
// Update the Texture of the Skybox with the one pointed by this zone
auto cubeMap = DependencyManager::get<TextureCache>()->getTexture(_bestZone->getSkyboxProperties().getURL(), CUBE_TEXTURE);
stage->getSkybox()->setCubemap(cubeMap->getGPUTexture());
}
stage->setBackgroundMode(model::SunSkyStage::SKY_BOX);
} else {
stage->setBackgroundMode(model::SunSkyStage::SKY_DOME); // let the application atmosphere through
}
}
} else {
if (_hasPreviousZone) {
scene->setKeyLightColor(_previousKeyLightColor);
scene->setKeyLightIntensity(_previousKeyLightIntensity);
scene->setKeyLightAmbientIntensity(_previousKeyLightAmbientIntensity);
scene->setKeyLightDirection(_previousKeyLightDirection);
scene->setStageSunModelEnable(_previousStageSunModelEnabled);
scene->setStageLocation(_previousStageLongitude, _previousStageLatitude,
_previousStageAltitude);
scene->setStageDayTime(_previousStageHour);
scene->setStageYearTime(_previousStageDay);
_hasPreviousZone = false;
}
_viewState->endOverrideEnvironmentData();
scene->getSkyStage()->setBackgroundMode(model::SunSkyStage::SKY_DOME); // let the application atmosphere through
}
// we must call endScene while we still have the tree locked so that no one deletes a model
// on us while rendering the scene
Model::endScene(renderMode, &args);
_tree->unlock();
// stats...
_meshesConsidered = args._meshesConsidered;
_meshesRendered = args._meshesRendered;
_meshesOutOfView = args._meshesOutOfView;
_meshesTooSmall = args._meshesTooSmall;
_elementsTouched = args._elementsTouched;
_itemsRendered = args._itemsRendered;
_itemsOutOfView = args._itemsOutOfView;
_itemsTooSmall = args._itemsTooSmall;
_materialSwitches = args._materialSwitches;
_trianglesRendered = args._trianglesRendered;
_quadsRendered = args._quadsRendered;
_translucentMeshPartsRendered = args._translucentMeshPartsRendered;
_opaqueMeshPartsRendered = args._opaqueMeshPartsRendered;
}
deleteReleasedModels(); // seems like as good as any other place to do some memory cleanup
}
@ -564,57 +556,36 @@ const FBXGeometry* EntityTreeRenderer::getCollisionGeometryForEntity(EntityItemP
return result;
}
void EntityTreeRenderer::renderElementProxy(EntityTreeElement* entityTreeElement) {
void EntityTreeRenderer::renderElementProxy(EntityTreeElement* entityTreeElement, RenderArgs* args) {
auto deferredLighting = DependencyManager::get<DeferredLightingEffect>();
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
Transform transform;
glm::vec3 elementCenter = entityTreeElement->getAACube().calcCenter();
float elementSize = entityTreeElement->getScale();
glPushMatrix();
glTranslatef(elementCenter.x, elementCenter.y, elementCenter.z);
DependencyManager::get<DeferredLightingEffect>()->renderWireCube(elementSize, glm::vec4(1.0f, 0.0f, 0.0f, 1.0f));
glPopMatrix();
auto drawWireCube = [&](glm::vec3 offset, float size, glm::vec4 color) {
transform.setTranslation(elementCenter + offset);
batch.setModelTransform(transform);
deferredLighting->renderWireCube(batch, size, color);
};
drawWireCube(glm::vec3(), elementSize, glm::vec4(1.0f, 0.0f, 0.0f, 1.0f));
if (_displayElementChildProxies) {
// draw the children
float halfSize = elementSize / 2.0f;
float quarterSize = elementSize / 4.0f;
glPushMatrix();
glTranslatef(elementCenter.x - quarterSize, elementCenter.y - quarterSize, elementCenter.z - quarterSize);
DependencyManager::get<DeferredLightingEffect>()->renderWireCube(halfSize, glm::vec4(1.0f, 1.0f, 0.0f, 1.0f));
glPopMatrix();
glPushMatrix();
glTranslatef(elementCenter.x + quarterSize, elementCenter.y - quarterSize, elementCenter.z - quarterSize);
DependencyManager::get<DeferredLightingEffect>()->renderWireCube(halfSize, glm::vec4(1.0f, 0.0f, 1.0f, 1.0f));
glPopMatrix();
glPushMatrix();
glTranslatef(elementCenter.x - quarterSize, elementCenter.y + quarterSize, elementCenter.z - quarterSize);
DependencyManager::get<DeferredLightingEffect>()->renderWireCube(halfSize, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f));
glPopMatrix();
glPushMatrix();
glTranslatef(elementCenter.x - quarterSize, elementCenter.y - quarterSize, elementCenter.z + quarterSize);
DependencyManager::get<DeferredLightingEffect>()->renderWireCube(halfSize, glm::vec4(0.0f, 0.0f, 1.0f, 1.0f));
glPopMatrix();
glPushMatrix();
glTranslatef(elementCenter.x + quarterSize, elementCenter.y + quarterSize, elementCenter.z + quarterSize);
DependencyManager::get<DeferredLightingEffect>()->renderWireCube(halfSize, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
glPopMatrix();
glPushMatrix();
glTranslatef(elementCenter.x - quarterSize, elementCenter.y + quarterSize, elementCenter.z + quarterSize);
DependencyManager::get<DeferredLightingEffect>()->renderWireCube(halfSize, glm::vec4(0.0f, 0.5f, 0.5f, 1.0f));
glPopMatrix();
glPushMatrix();
glTranslatef(elementCenter.x + quarterSize, elementCenter.y - quarterSize, elementCenter.z + quarterSize);
DependencyManager::get<DeferredLightingEffect>()->renderWireCube(halfSize, glm::vec4(0.5f, 0.0f, 0.0f, 1.0f));
glPopMatrix();
glPushMatrix();
glTranslatef(elementCenter.x + quarterSize, elementCenter.y + quarterSize, elementCenter.z - quarterSize);
DependencyManager::get<DeferredLightingEffect>()->renderWireCube(halfSize, glm::vec4(0.0f, 0.5f, 0.0f, 1.0f));
glPopMatrix();
drawWireCube(glm::vec3(-quarterSize, -quarterSize, -quarterSize), halfSize, glm::vec4(1.0f, 1.0f, 0.0f, 1.0f));
drawWireCube(glm::vec3(quarterSize, -quarterSize, -quarterSize), halfSize, glm::vec4(1.0f, 0.0f, 1.0f, 1.0f));
drawWireCube(glm::vec3(-quarterSize, quarterSize, -quarterSize), halfSize, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f));
drawWireCube(glm::vec3(-quarterSize, -quarterSize, quarterSize), halfSize, glm::vec4(0.0f, 0.0f, 1.0f, 1.0f));
drawWireCube(glm::vec3(quarterSize, quarterSize, quarterSize), halfSize, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
drawWireCube(glm::vec3(-quarterSize, quarterSize, quarterSize), halfSize, glm::vec4(0.0f, 0.5f, 0.5f, 1.0f));
drawWireCube(glm::vec3(quarterSize, -quarterSize, quarterSize), halfSize, glm::vec4(0.5f, 0.0f, 0.0f, 1.0f));
drawWireCube(glm::vec3(quarterSize, quarterSize, -quarterSize), halfSize, glm::vec4(0.0f, 0.5f, 0.0f, 1.0f));
}
}
@ -631,48 +602,35 @@ void EntityTreeRenderer::renderProxies(EntityItemPointer entity, RenderArgs* arg
glm::vec3 minCenter = minCube.calcCenter();
glm::vec3 entityBoxCenter = entityBox.calcCenter();
glm::vec3 entityBoxScale = entityBox.getScale();
auto deferredLighting = DependencyManager::get<DeferredLightingEffect>();
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
Transform transform;
// draw the max bounding cube
glPushMatrix();
glTranslatef(maxCenter.x, maxCenter.y, maxCenter.z);
DependencyManager::get<DeferredLightingEffect>()->renderWireCube(maxCube.getScale(), glm::vec4(1.0f, 1.0f, 0.0f, 1.0f));
glPopMatrix();
transform.setTranslation(maxCenter);
batch.setModelTransform(transform);
deferredLighting->renderWireCube(batch, maxCube.getScale(), glm::vec4(1.0f, 1.0f, 0.0f, 1.0f));
// draw the min bounding cube
glPushMatrix();
glTranslatef(minCenter.x, minCenter.y, minCenter.z);
DependencyManager::get<DeferredLightingEffect>()->renderWireCube(minCube.getScale(), glm::vec4(0.0f, 1.0f, 0.0f, 1.0f));
glPopMatrix();
transform.setTranslation(minCenter);
batch.setModelTransform(transform);
deferredLighting->renderWireCube(batch, minCube.getScale(), glm::vec4(0.0f, 1.0f, 0.0f, 1.0f));
// draw the entityBox bounding box
glPushMatrix();
glTranslatef(entityBoxCenter.x, entityBoxCenter.y, entityBoxCenter.z);
glScalef(entityBoxScale.x, entityBoxScale.y, entityBoxScale.z);
DependencyManager::get<DeferredLightingEffect>()->renderWireCube(1.0f, glm::vec4(0.0f, 0.0f, 1.0f, 1.0f));
glPopMatrix();
transform.setTranslation(entityBoxCenter);
transform.setScale(entityBoxScale);
batch.setModelTransform(transform);
deferredLighting->renderWireCube(batch, 1.0f, glm::vec4(0.0f, 0.0f, 1.0f, 1.0f));
glm::vec3 position = entity->getPosition();
glm::vec3 center = entity->getCenter();
glm::vec3 dimensions = entity->getDimensions();
glm::quat rotation = entity->getRotation();
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glPushMatrix();
glm::vec3 positionToCenter = center - position;
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
glScalef(dimensions.x, dimensions.y, dimensions.z);
DependencyManager::get<DeferredLightingEffect>()->renderWireCube(1.0f, glm::vec4(1.0f, 0.0f, 1.0f, 1.0f));
glPopMatrix();
glPopMatrix();
// Rotated bounding box
batch.setModelTransform(entity->getTransformToCenter());
deferredLighting->renderWireCube(batch, 1.0f, glm::vec4(1.0f, 0.0f, 1.0f, 1.0f));
}
}
void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) {
args->_elementsTouched++;
// actually render it here...
// we need to iterate the actual entityItems of the element
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
@ -685,7 +643,7 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
bool isShadowMode = args->_renderMode == RenderArgs::SHADOW_RENDER_MODE;
if (!isShadowMode && _displayModelElementProxy && numberOfEntities > 0) {
renderElementProxy(entityTreeElement);
renderElementProxy(entityTreeElement, args);
}
for (uint16_t i = 0; i < numberOfEntities; i++) {
@ -715,36 +673,6 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
}
}
}
// render entityItem
AABox entityBox = entityItem->getAABox();
// TODO: some entity types (like lights) might want to be rendered even
// when they are outside of the view frustum...
float distance = args->_viewFrustum->distanceToCamera(entityBox.calcCenter());
bool outOfView = args->_viewFrustum->boxInFrustum(entityBox) == ViewFrustum::OUTSIDE;
if (!outOfView) {
bool bigEnoughToRender = _viewState->shouldRenderMesh(entityBox.getLargestDimension(), distance);
if (bigEnoughToRender) {
renderProxies(entityItem, args);
Glower* glower = NULL;
if (entityItem->getGlowLevel() > 0.0f) {
glower = new Glower(entityItem->getGlowLevel());
}
entityItem->render(args);
args->_itemsRendered++;
if (glower) {
delete glower;
}
} else {
args->_itemsTooSmall++;
}
} else {
args->_itemsOutOfView++;
}
}
}
}
@ -1070,12 +998,34 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
checkAndCallUnload(entityID);
}
_entityScripts.remove(entityID);
// here's where we remove the entity payload from the scene
if (_entitiesInScene.contains(entityID)) {
auto entity = _entitiesInScene.take(entityID);
render::PendingChanges pendingChanges;
auto scene = _viewState->getMain3DScene();
entity->removeFromScene(entity, scene, pendingChanges);
scene->enqueuePendingChanges(pendingChanges);
}
}
void EntityTreeRenderer::addingEntity(const EntityItemID& entityID) {
checkAndCallPreload(entityID);
auto entity = static_cast<EntityTree*>(_tree)->findEntityByID(entityID);
addEntityToScene(entity);
}
void EntityTreeRenderer::addEntityToScene(EntityItemPointer entity) {
// here's where we add the entity payload to the scene
render::PendingChanges pendingChanges;
auto scene = _viewState->getMain3DScene();
if (entity->addToScene(entity, scene, pendingChanges)) {
_entitiesInScene.insert(entity->getEntityItemID(), entity);
}
scene->enqueuePendingChanges(pendingChanges);
}
void EntityTreeRenderer::entitySciptChanging(const EntityItemID& entityID) {
if (_tree && !_shuttingDown) {
checkAndCallUnload(entityID);
@ -1244,4 +1194,3 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons
entityScriptB.property("collisionWithEntity").call(entityScriptA, args);
}
}

View file

@ -58,9 +58,7 @@ public:
void processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode);
virtual void init();
virtual void render(RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE,
RenderArgs::RenderSide renderSide = RenderArgs::MONO,
RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE);
virtual void render(RenderArgs* renderArgs) override;
virtual const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem);
virtual const Model* getModelForEntityItem(EntityItemPointer entityItem);
@ -125,7 +123,10 @@ protected:
virtual Octree* createTree() { return new EntityTree(true); }
private:
void renderElementProxy(EntityTreeElement* entityTreeElement);
void addEntityToScene(EntityItemPointer entity);
void applyZonePropertiesToScene(std::shared_ptr<ZoneEntityItem> zone);
void renderElementProxy(EntityTreeElement* entityTreeElement, RenderArgs* args);
void checkAndCallPreload(const EntityItemID& entityID);
void checkAndCallUnload(const EntityItemID& entityID);
@ -187,6 +188,8 @@ private:
float _previousStageHour;
int _previousStageDay;
QHash<EntityItemID, EntityItemPointer> _entitiesInScene;
};
#endif // hifi_EntityTreeRenderer_h

View file

@ -12,6 +12,7 @@
#include <glm/gtx/quaternion.hpp>
#include <gpu/GPUConfig.h>
#include <gpu/Batch.h>
#include <DeferredLightingEffect.h>
#include <PerfStat.h>
@ -24,17 +25,8 @@ EntityItemPointer RenderableBoxEntityItem::factory(const EntityItemID& entityID,
void RenderableBoxEntityItem::render(RenderArgs* args) {
PerformanceTimer perfTimer("RenderableBoxEntityItem::render");
assert(getType() == EntityTypes::Box);
glm::vec3 position = getPosition();
glm::vec3 center = getCenter();
glm::vec3 dimensions = getDimensions();
glm::quat rotation = getRotation();
const float MAX_COLOR = 255.0f;
glm::vec4 cubeColor(getColor()[RED_INDEX] / MAX_COLOR, getColor()[GREEN_INDEX] / MAX_COLOR,
getColor()[BLUE_INDEX] / MAX_COLOR, getLocalRenderAlpha());
Q_ASSERT(getType() == EntityTypes::Box);
glm::vec4 cubeColor(toGlm(getXColor()), getLocalRenderAlpha());
bool debugSimulationOwnership = args->_debugFlags & RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP;
bool highlightSimulationOwnership = false;
@ -43,22 +35,15 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
const QUuid& myNodeID = nodeList->getSessionUUID();
highlightSimulationOwnership = (getSimulatorID() == myNodeID);
}
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glPushMatrix();
glm::vec3 positionToCenter = center - position;
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
glScalef(dimensions.x, dimensions.y, dimensions.z);
if (highlightSimulationOwnership) {
DependencyManager::get<DeferredLightingEffect>()->renderWireCube(1.0f, cubeColor);
} else {
DependencyManager::get<DeferredLightingEffect>()->renderSolidCube(1.0f, cubeColor);
}
glPopMatrix();
glPopMatrix();
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
batch.setModelTransform(getTransformToCenter());
if (highlightSimulationOwnership) {
DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.0f, cubeColor);
} else {
DependencyManager::get<DeferredLightingEffect>()->renderSolidCube(batch, 1.0f, cubeColor);
}
RenderableDebugableEntityItem::render(this, args);
};

View file

@ -14,6 +14,7 @@
#include <BoxEntityItem.h>
#include "RenderableDebugableEntityItem.h"
#include "RenderableEntityItem.h"
class RenderableBoxEntityItem : public BoxEntityItem {
public:
@ -24,6 +25,8 @@ public:
{ }
virtual void render(RenderArgs* args);
SIMPLE_RENDERABLE()
};

View file

@ -12,7 +12,10 @@
#include <glm/gtx/quaternion.hpp>
#include <gpu/GPUConfig.h>
#include <gpu/Batch.h>
#include <DeferredLightingEffect.h>
#include <PhysicsEngine.h>
@ -21,48 +24,24 @@
void RenderableDebugableEntityItem::renderBoundingBox(EntityItem* entity, RenderArgs* args,
float puffedOut, glm::vec4& color) {
glm::vec3 position = entity->getPosition();
glm::vec3 center = entity->getCenter();
glm::vec3 dimensions = entity->getDimensions();
glm::quat rotation = entity->getRotation();
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glPushMatrix();
glm::vec3 positionToCenter = center - position;
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
glScalef(dimensions.x, dimensions.y, dimensions.z);
DependencyManager::get<DeferredLightingEffect>()->renderWireCube(1.0f + puffedOut, color);
glPopMatrix();
glPopMatrix();
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
batch.setModelTransform(entity->getTransformToCenter());
DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.0f + puffedOut, color);
}
void RenderableDebugableEntityItem::renderHoverDot(EntityItem* entity, RenderArgs* args) {
glm::vec3 position = entity->getPosition();
glm::vec3 center = entity->getCenter();
glm::vec3 dimensions = entity->getDimensions();
glm::quat rotation = entity->getRotation();
glm::vec4 blueColor(0.0f, 0.0f, 1.0f, 1.0f);
const int SLICES = 8, STACKS = 8;
float radius = 0.05f;
glPushMatrix();
glTranslatef(position.x, position.y + dimensions.y + radius, position.z);
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glPushMatrix();
glm::vec3 positionToCenter = center - position;
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
glScalef(radius, radius, radius);
const int SLICES = 8;
const int STACKS = 8;
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(0.5f, SLICES, STACKS, blueColor);
glPopMatrix();
glPopMatrix();
glm::vec4 blueColor(0.0f, 0.0f, 1.0f, 1.0f);
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
Transform transform = entity->getTransformToCenter();
// Cancel true dimensions and set scale to 2 * radius (diameter)
transform.postScale(2.0f * glm::vec3(radius, radius, radius) / entity->getDimensions());
batch.setModelTransform(transform);
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(batch, 0.5f, SLICES, STACKS, blueColor);
}
void RenderableDebugableEntityItem::render(EntityItem* entity, RenderArgs* args) {

View file

@ -0,0 +1,41 @@
//
// RenderableEntityItem.cpp
// interface/src
//
// Created by Brad Hefta-Gaub on 12/6/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "RenderableEntityItem.h"
namespace render {
template <> const ItemKey payloadGetKey(const RenderableEntityItemProxy::Pointer& payload) {
if (payload && payload->entity) {
if (payload->entity->getType() == EntityTypes::Light) {
return ItemKey::Builder::light();
}
}
return ItemKey::Builder::opaqueShape();
}
template <> const Item::Bound payloadGetBound(const RenderableEntityItemProxy::Pointer& payload) {
if (payload && payload->entity) {
return payload->entity->getAABox();
}
return render::Item::Bound();
}
template <> void payloadRender(const RenderableEntityItemProxy::Pointer& payload, RenderArgs* args) {
if (args) {
if (payload && payload->entity && payload->entity->getVisible()) {
payload->entity->render(args);
}
}
}
}

View file

@ -0,0 +1,66 @@
//
// RenderableEntityItem.h
// interface/src
//
// Created by Brad Hefta-Gaub on 12/6/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_RenderableEntityItem_h
#define hifi_RenderableEntityItem_h
#include <render/Scene.h>
#include <EntityItem.h>
class RenderableEntityItemProxy {
public:
RenderableEntityItemProxy(EntityItemPointer entity) : entity(entity) { }
typedef render::Payload<RenderableEntityItemProxy> Payload;
typedef Payload::DataPointer Pointer;
EntityItemPointer entity;
};
namespace render {
template <> const ItemKey payloadGetKey(const RenderableEntityItemProxy::Pointer& payload);
template <> const Item::Bound payloadGetBound(const RenderableEntityItemProxy::Pointer& payload);
template <> void payloadRender(const RenderableEntityItemProxy::Pointer& payload, RenderArgs* args);
}
// Mixin class for implementing basic single item rendering
class SimpleRenderableEntityItem {
public:
bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
_myItem = scene->allocateID();
auto renderData = RenderableEntityItemProxy::Pointer(new RenderableEntityItemProxy(self));
auto renderPayload = render::PayloadPointer(new RenderableEntityItemProxy::Payload(renderData));
pendingChanges.resetItem(_myItem, renderPayload);
return true;
}
void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
pendingChanges.removeItem(_myItem);
}
private:
render::ItemID _myItem;
};
#define SIMPLE_RENDERABLE() \
public: \
virtual bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) { return _renderHelper.addToScene(self, scene, pendingChanges); } \
virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) { _renderHelper.removeFromScene(self, scene, pendingChanges); } \
private: \
SimpleRenderableEntityItem _renderHelper;
#endif // hifi_RenderableEntityItem_h

View file

@ -12,6 +12,7 @@
#include <glm/gtx/quaternion.hpp>
#include <gpu/GPUConfig.h>
#include <gpu/Batch.h>
#include <DeferredLightingEffect.h>
#include <GLMHelpers.h>
@ -31,12 +32,7 @@ void RenderableLightEntityItem::render(RenderArgs* args) {
glm::quat rotation = getRotation();
float largestDiameter = glm::max(dimensions.x, dimensions.y, dimensions.z);
const float MAX_COLOR = 255.0f;
float colorR = getColor()[RED_INDEX] / MAX_COLOR;
float colorG = getColor()[GREEN_INDEX] / MAX_COLOR;
float colorB = getColor()[BLUE_INDEX] / MAX_COLOR;
glm::vec3 color = glm::vec3(colorR, colorG, colorB);
glm::vec3 color = toGlm(getXColor());
float intensity = getIntensity();
float exponent = getExponent();
@ -49,21 +45,12 @@ void RenderableLightEntityItem::render(RenderArgs* args) {
DependencyManager::get<DeferredLightingEffect>()->addPointLight(position, largestDiameter / 2.0f,
color, intensity);
}
#ifdef WANT_DEBUG
glm::vec4 color(diffuseR, diffuseG, diffuseB, 1.0f);
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glPushMatrix();
glm::vec3 positionToCenter = center - position;
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
glScalef(dimensions.x, dimensions.y, dimensions.z);
DependencyManager::get<DeferredLightingEffect>()->renderWireSphere(0.5f, 15, 15, color);
glPopMatrix();
glPopMatrix();
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
batch.setModelTransform(getTransformToCenter());
DependencyManager::get<DeferredLightingEffect>()->renderWireSphere(batch, 0.5f, 15, 15, glm::vec4(color, 1.0f));
#endif
};

View file

@ -13,6 +13,7 @@
#define hifi_RenderableLightEntityItem_h
#include <LightEntityItem.h>
#include "RenderableEntityItem.h"
class RenderableLightEntityItem : public LightEntityItem {
public:
@ -27,6 +28,8 @@ public:
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
void** intersectedObject, bool precisionPicking) const;
SIMPLE_RENDERABLE();
};

View file

@ -12,6 +12,7 @@
#include <glm/gtx/quaternion.hpp>
#include <gpu/GPUConfig.h>
#include <gpu/Batch.h>
#include <GeometryCache.h>
#include <DeferredLightingEffect.h>
@ -23,32 +24,34 @@ EntityItemPointer RenderableLineEntityItem::factory(const EntityItemID& entityID
return EntityItemPointer(new RenderableLineEntityItem(entityID, properties));
}
void RenderableLineEntityItem::updateGeometry() {
auto geometryCache = DependencyManager::get<GeometryCache>();
if (_lineVerticesID == GeometryCache::UNKNOWN_ID) {
_lineVerticesID = geometryCache ->allocateID();
}
if (_pointsChanged) {
glm::vec4 lineColor(toGlm(getXColor()), getLocalRenderAlpha());
geometryCache->updateVertices(_lineVerticesID, getLinePoints(), lineColor);
_pointsChanged = false;
}
}
void RenderableLineEntityItem::render(RenderArgs* args) {
PerformanceTimer perfTimer("RenderableLineEntityItem::render");
assert(getType() == EntityTypes::Line);
// glm::vec3 position = getPosition();
// glm::vec3 dimensions = getDimensions();
glm::quat rotation = getRotation();
glm::vec4 lineColor(toGlm(getXColor()), getLocalRenderAlpha());
glPushMatrix();
glLineWidth(getLineWidth());
auto geometryCache = DependencyManager::get<GeometryCache>();
if (_lineVerticesID == GeometryCache::UNKNOWN_ID) {
_lineVerticesID = geometryCache ->allocateID();
}
//TODO: Figure out clean , efficient way to do relative line positioning. For now we'll just use absolute positioning.
//glTranslatef(position.x, position.y, position.z);
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
if (_pointsChanged) {
geometryCache->updateVertices(_lineVerticesID, getLinePoints(), lineColor);
_pointsChanged = false;
}
if (getLinePoints().size() > 1) {
geometryCache->renderVertices(gpu::LINE_STRIP, _lineVerticesID);
}
glPopMatrix();
Q_ASSERT(getType() == EntityTypes::Line);
updateGeometry();
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
// TODO: Figure out clean , efficient way to do relative line positioning. For now we'll just use absolute positioning.
//batch.setModelTransform(getTransformToCenter());
batch.setModelTransform(Transform());
batch._glLineWidth(getLineWidth());
if (getLinePoints().size() > 1) {
DependencyManager::get<GeometryCache>()->renderVertices(batch, gpu::LINE_STRIP, _lineVerticesID);
}
batch._glLineWidth(1.0f);
RenderableDebugableEntityItem::render(this, args);
};

View file

@ -14,6 +14,7 @@
#include <LineEntityItem.h>
#include "RenderableDebugableEntityItem.h"
#include "RenderableEntityItem.h"
#include <GeometryCache.h>
class RenderableLineEntityItem : public LineEntityItem {
@ -27,7 +28,11 @@ public:
virtual void render(RenderArgs* args);
SIMPLE_RENDERABLE();
protected:
void updateGeometry();
int _lineVerticesID;
};

View file

@ -15,9 +15,11 @@
#include <QJsonDocument>
#include <AbstractViewStateInterface.h>
#include <DeferredLightingEffect.h>
#include <Model.h>
#include <PerfStat.h>
#include <render/Scene.h>
#include "EntityTreeRenderer.h"
#include "EntitiesRendererLogging.h"
@ -108,6 +110,90 @@ void RenderableModelEntityItem::remapTextures() {
_currentTextures = _textures;
}
// TODO: we need a solution for changes to the postion/rotation/etc of a model...
// this current code path only addresses that in this setup case... not the changing/moving case
bool RenderableModelEntityItem::readyToAddToScene(RenderArgs* renderArgs) {
if (!_model && renderArgs) {
// TODO: this getModel() appears to be about 3% of model render time. We should optimize
PerformanceTimer perfTimer("getModel");
EntityTreeRenderer* renderer = static_cast<EntityTreeRenderer*>(renderArgs->_renderer);
getModel(renderer);
}
if (renderArgs && _model && _needsInitialSimulation && _model->isActive() && _model->isLoadedWithTextures()) {
_model->setScaleToFit(true, getDimensions());
_model->setSnapModelToRegistrationPoint(true, getRegistrationPoint());
_model->setRotation(getRotation());
_model->setTranslation(getPosition());
// make sure to simulate so everything gets set up correctly for rendering
{
PerformanceTimer perfTimer("_model->simulate");
_model->simulate(0.0f);
}
_needsInitialSimulation = false;
_model->renderSetup(renderArgs);
}
bool ready = !_needsInitialSimulation && _model && _model->readyToAddToScene(renderArgs);
return ready;
}
class RenderableModelEntityItemMeta {
public:
RenderableModelEntityItemMeta(EntityItemPointer entity) : entity(entity){ }
typedef render::Payload<RenderableModelEntityItemMeta> Payload;
typedef Payload::DataPointer Pointer;
EntityItemPointer entity;
};
namespace render {
template <> const ItemKey payloadGetKey(const RenderableModelEntityItemMeta::Pointer& payload) {
return ItemKey::Builder::opaqueShape();
}
template <> const Item::Bound payloadGetBound(const RenderableModelEntityItemMeta::Pointer& payload) {
if (payload && payload->entity) {
return payload->entity->getAABox();
}
return render::Item::Bound();
}
template <> void payloadRender(const RenderableModelEntityItemMeta::Pointer& payload, RenderArgs* args) {
if (args) {
if (payload && payload->entity) {
payload->entity->render(args);
}
}
}
}
bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges) {
_myMetaItem = scene->allocateID();
auto renderData = RenderableModelEntityItemMeta::Pointer(new RenderableModelEntityItemMeta(self));
auto renderPayload = render::PayloadPointer(new RenderableModelEntityItemMeta::Payload(renderData));
pendingChanges.resetItem(_myMetaItem, renderPayload);
if (_model) {
return _model->addToScene(scene, pendingChanges);
}
return true;
}
void RenderableModelEntityItem::removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges) {
pendingChanges.removeItem(_myMetaItem);
if (_model) {
_model->removeFromScene(scene, pendingChanges);
}
}
// NOTE: this only renders the "meta" portion of the Model, namely it renders debugging items, and it handles
// the per frame simulation/update that might be required if the models properties changed.
void RenderableModelEntityItem::render(RenderArgs* args) {
PerformanceTimer perfTimer("RMEIrender");
assert(getType() == EntityTypes::Model);
@ -124,10 +210,29 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
}
if (hasModel()) {
if (_model) {
if (QUrl(getModelURL()) != _model->getURL()) {
qDebug() << "Updating model URL: " << getModelURL();
_model->setURL(getModelURL());
}
// check to see if when we added our models to the scene they were ready, if they were not ready, then
// fix them up in the scene
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
render::PendingChanges pendingChanges;
if (_model->needsFixupInScene()) {
_model->removeFromScene(scene, pendingChanges);
_model->addToScene(scene, pendingChanges);
}
scene->enqueuePendingChanges(pendingChanges);
_model->setVisibleInScene(getVisible(), scene);
}
remapTextures();
glPushMatrix();
{
float alpha = getLocalRenderAlpha();
// float alpha = getLocalRenderAlpha();
if (!_model || _needsModelReload) {
// TODO: this getModel() appears to be about 3% of model render time. We should optimize
@ -167,23 +272,8 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
}
_needsInitialSimulation = false;
}
if (_model->isActive()) {
// TODO: this is the majority of model render time. And rendering of a cube model vs the basic Box render
// is significantly more expensive. Is there a way to call this that doesn't cost us as much?
PerformanceTimer perfTimer("model->render");
// filter out if not needed to render
if (args && (args->_renderMode == RenderArgs::SHADOW_RENDER_MODE)) {
if (movingOrAnimating) {
_model->renderInScene(alpha, args);
}
} else {
_model->renderInScene(alpha, args);
}
}
}
}
glPopMatrix();
if (highlightSimulationOwnership) {
glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f);
@ -199,6 +289,10 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) {
Model* result = NULL;
if (!renderer) {
return result;
}
// make sure our renderer is setup
if (!_myRenderer) {
@ -206,7 +300,7 @@ Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) {
}
assert(_myRenderer == renderer); // you should only ever render on one renderer
if (QThread::currentThread() != _myRenderer->thread()) {
if (!_myRenderer || QThread::currentThread() != _myRenderer->thread()) {
return _model;
}
@ -394,7 +488,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
// to the visual model and apply them to the collision model (without regard for the
// collision model's extents).
glm::vec3 scale = _dimensions / renderGeometry.getUnscaledMeshExtents().size();
glm::vec3 scale = getDimensions() / renderGeometry.getUnscaledMeshExtents().size();
// multiply each point by scale before handing the point-set off to the physics engine.
// also determine the extents of the collision model.
AABox box;

View file

@ -43,6 +43,11 @@ public:
virtual void somethingChangedNotification() { _needsInitialSimulation = true; }
virtual bool readyToAddToScene(RenderArgs* renderArgs = nullptr);
virtual bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
virtual void render(RenderArgs* args);
virtual bool supportsDetailedRayIntersection() const { return true; }
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
@ -71,6 +76,8 @@ private:
QStringList _originalTextures;
bool _originalTexturesRead;
QVector<QVector<glm::vec3>> _points;
render::ItemID _myMetaItem;
};
#endif // hifi_RenderableModelEntityItem_h

View file

@ -30,8 +30,7 @@ RenderableParticleEffectEntityItem::RenderableParticleEffectEntityItem(const Ent
}
void RenderableParticleEffectEntityItem::render(RenderArgs* args) {
assert(getType() == EntityTypes::ParticleEffect);
Q_ASSERT(getType() == EntityTypes::ParticleEffect);
PerformanceTimer perfTimer("RenderableParticleEffectEntityItem::render");
if (_texturesChangedFlag) {
@ -45,143 +44,67 @@ void RenderableParticleEffectEntityItem::render(RenderArgs* args) {
_texturesChangedFlag = false;
}
if (!_texture) {
renderUntexturedQuads(args);
} else if (_texture && !_texture->isLoaded()) {
renderUntexturedQuads(args);
} else {
renderTexturedQuads(args);
bool textured = _texture && _texture->isLoaded();
updateQuads(args, textured);
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
if (textured) {
batch.setUniformTexture(0, _texture->getGPUTexture());
}
batch.setModelTransform(getTransformToCenter());
DependencyManager::get<GeometryCache>()->renderVertices(batch, gpu::QUADS, _cacheID);
};
void RenderableParticleEffectEntityItem::renderUntexturedQuads(RenderArgs* args) {
float particleRadius = getParticleRadius();
const float MAX_COLOR = 255.0f;
glm::vec4 particleColor(getColor()[RED_INDEX] / MAX_COLOR,
getColor()[GREEN_INDEX] / MAX_COLOR,
getColor()[BLUE_INDEX] / MAX_COLOR,
getLocalRenderAlpha());
glm::vec3 upOffset = args->_viewFrustum->getUp() * particleRadius;
glm::vec3 rightOffset = args->_viewFrustum->getRight() * particleRadius;
QVector<glm::vec3> vertices(getLivingParticleCount() * VERTS_PER_PARTICLE);
quint32 count = 0;
for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) {
glm::vec3 pos = _particlePositions[i];
// generate corners of quad, aligned to face the camera
vertices.append(pos - rightOffset + upOffset);
vertices.append(pos + rightOffset + upOffset);
vertices.append(pos + rightOffset - upOffset);
vertices.append(pos - rightOffset - upOffset);
count++;
}
// just double checking, cause if this invarient is false, we might have memory corruption bugs.
assert(count == getLivingParticleCount());
// update geometry cache with all the verts in model coordinates.
DependencyManager::get<GeometryCache>()->updateVertices(_cacheID, vertices, particleColor);
glPushMatrix();
glm::vec3 position = getPosition();
glTranslatef(position.x, position.y, position.z);
glm::quat rotation = getRotation();
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glPushMatrix();
glm::vec3 positionToCenter = getCenter() - position;
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
DependencyManager::get<GeometryCache>()->renderVertices(gpu::QUADS, _cacheID);
glPopMatrix();
glPopMatrix();
}
static glm::vec3 zSortAxis;
static bool zSort(const glm::vec3& rhs, const glm::vec3& lhs) {
return glm::dot(rhs, ::zSortAxis) > glm::dot(lhs, ::zSortAxis);
}
void RenderableParticleEffectEntityItem::renderTexturedQuads(RenderArgs* args) {
void RenderableParticleEffectEntityItem::updateQuads(RenderArgs* args, bool textured) {
float particleRadius = getParticleRadius();
const float MAX_COLOR = 255.0f;
glm::vec4 particleColor(getColor()[RED_INDEX] / MAX_COLOR,
getColor()[GREEN_INDEX] / MAX_COLOR,
getColor()[BLUE_INDEX] / MAX_COLOR,
getLocalRenderAlpha());
QVector<glm::vec3> positions(getLivingParticleCount());
quint32 count = 0;
for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) {
positions.append(_particlePositions[i]);
count++;
}
// just double checking, cause if this invarient is false, we might have memory corruption bugs.
assert(count == getLivingParticleCount());
// sort particles back to front
::zSortAxis = args->_viewFrustum->getDirection();
qSort(positions.begin(), positions.end(), zSort);
QVector<glm::vec3> vertices(getLivingParticleCount() * VERTS_PER_PARTICLE);
QVector<glm::vec2> textureCoords(getLivingParticleCount() * VERTS_PER_PARTICLE);
glm::vec4 particleColor(toGlm(getXColor()), getLocalRenderAlpha());
glm::vec3 upOffset = args->_viewFrustum->getUp() * particleRadius;
glm::vec3 rightOffset = args->_viewFrustum->getRight() * particleRadius;
QVector<glm::vec3> vertices;
QVector<glm::vec3> positions;
QVector<glm::vec2> textureCoords;
vertices.reserve(getLivingParticleCount() * VERTS_PER_PARTICLE);
if (textured) {
positions.reserve(getLivingParticleCount());
textureCoords.reserve(getLivingParticleCount() * VERTS_PER_PARTICLE);
for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) {
positions.append(_particlePositions[i]);
textureCoords.append(glm::vec2(0, 1));
textureCoords.append(glm::vec2(1, 1));
textureCoords.append(glm::vec2(1, 0));
textureCoords.append(glm::vec2(0, 0));
}
// sort particles back to front
::zSortAxis = args->_viewFrustum->getDirection();
qSort(positions.begin(), positions.end(), zSort);
}
for (int i = 0; i < positions.size(); i++) {
glm::vec3 pos = positions[i];
glm::vec3 pos = (textured) ? positions[i] : _particlePositions[i];
// generate corners of quad aligned to face the camera.
vertices.append(pos - rightOffset + upOffset);
vertices.append(pos + rightOffset + upOffset);
vertices.append(pos + rightOffset - upOffset);
vertices.append(pos - rightOffset - upOffset);
textureCoords.append(glm::vec2(0, 1));
textureCoords.append(glm::vec2(1, 1));
textureCoords.append(glm::vec2(1, 0));
textureCoords.append(glm::vec2(0, 0));
}
// update geometry cache with all the verts in model coordinates.
DependencyManager::get<GeometryCache>()->updateVertices(_cacheID, vertices, textureCoords, particleColor);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, _texture->getID());
glPushMatrix();
glm::vec3 position = getPosition();
glTranslatef(position.x, position.y, position.z);
glm::quat rotation = getRotation();
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glPushMatrix();
glm::vec3 positionToCenter = getCenter() - position;
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
DependencyManager::get<GeometryCache>()->renderVertices(gpu::QUADS, _cacheID);
glPopMatrix();
glPopMatrix();
glDisable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
if (textured) {
DependencyManager::get<GeometryCache>()->updateVertices(_cacheID, vertices, textureCoords, particleColor);
} else {
DependencyManager::get<GeometryCache>()->updateVertices(_cacheID, vertices, particleColor);
}
}

View file

@ -13,6 +13,7 @@
#include <ParticleEffectEntityItem.h>
#include <TextureCache.h>
#include "RenderableEntityItem.h"
class RenderableParticleEffectEntityItem : public ParticleEffectEntityItem {
public:
@ -20,8 +21,9 @@ public:
RenderableParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
virtual void render(RenderArgs* args);
void renderUntexturedQuads(RenderArgs* args);
void renderTexturedQuads(RenderArgs* args);
void updateQuads(RenderArgs* args, bool textured);
SIMPLE_RENDERABLE();
protected:

View file

@ -170,7 +170,7 @@ void RenderablePolyVoxEntityItem::setVoxelData(QByteArray voxelData) {
}
glm::vec3 RenderablePolyVoxEntityItem::getSurfacePositionAdjustment() const {
glm::vec3 scale = _dimensions / _voxelVolumeSize; // meters / voxel-units
glm::vec3 scale = getDimensions() / _voxelVolumeSize; // meters / voxel-units
switch (_voxelSurfaceStyle) {
case PolyVoxEntityItem::SURFACE_MARCHING_CUBES:
return scale / 2.0f;
@ -183,18 +183,18 @@ glm::vec3 RenderablePolyVoxEntityItem::getSurfacePositionAdjustment() const {
}
glm::mat4 RenderablePolyVoxEntityItem::voxelToLocalMatrix() const {
glm::vec3 scale = _dimensions / _voxelVolumeSize; // meters / voxel-units
glm::vec3 center = getCenter();
glm::vec3 scale = getDimensions() / _voxelVolumeSize; // meters / voxel-units
glm::vec3 center = getCenterPosition();
glm::vec3 position = getPosition();
glm::vec3 positionToCenter = center - position;
positionToCenter -= _dimensions * glm::vec3(0.5f, 0.5f, 0.5f) - getSurfacePositionAdjustment();
positionToCenter -= getDimensions() * glm::vec3(0.5f, 0.5f, 0.5f) - getSurfacePositionAdjustment();
glm::mat4 centerToCorner = glm::translate(glm::mat4(), positionToCenter);
glm::mat4 scaled = glm::scale(centerToCorner, scale);
return scaled;
}
glm::mat4 RenderablePolyVoxEntityItem::voxelToWorldMatrix() const {
glm::mat4 rotation = glm::mat4_cast(_rotation);
glm::mat4 rotation = glm::mat4_cast(getRotation());
glm::mat4 translation = glm::translate(getPosition());
return translation * rotation * voxelToLocalMatrix();
}
@ -213,7 +213,7 @@ uint8_t RenderablePolyVoxEntityItem::getVoxel(int x, int y, int z) {
// if _voxelSurfaceStyle is SURFACE_EDGED_CUBIC, we maintain an extra layer of
// voxels all around the requested voxel space. Having the empty voxels around
// the edges changes how the surface extractor behaves.
if (_voxelSurfaceStyle == SURFACE_EDGED_CUBIC) {
return _volData->getVoxelAt(x + 1, y + 1, z + 1);
}
@ -239,7 +239,7 @@ void RenderablePolyVoxEntityItem::updateOnCount(int x, int y, int z, uint8_t toV
if (!inUserBounds(_volData, _voxelSurfaceStyle, x, y, z)) {
return;
}
uint8_t uVoxelValue = getVoxel(x, y, z);
if (toValue != 0) {
if (uVoxelValue == 0) {
@ -294,7 +294,7 @@ void RenderablePolyVoxEntityItem::setSphereInVolume(glm::vec3 center, float radi
void RenderablePolyVoxEntityItem::setSphere(glm::vec3 centerWorldCoords, float radiusWorldCoords, uint8_t toValue) {
// glm::vec3 centerVoxelCoords = worldToVoxelCoordinates(centerWorldCoords);
glm::vec4 centerVoxelCoords = worldToVoxelMatrix() * glm::vec4(centerWorldCoords, 1.0f);
glm::vec3 scale = _dimensions / _voxelVolumeSize; // meters / voxel-units
glm::vec3 scale = getDimensions() / _voxelVolumeSize; // meters / voxel-units
float scaleY = scale.y;
float radiusVoxelCoords = radiusWorldCoords / scaleY;
setSphereInVolume(glm::vec3(centerVoxelCoords), radiusVoxelCoords, toValue);
@ -347,8 +347,8 @@ void RenderablePolyVoxEntityItem::getModel() {
sizeof(PolyVox::PositionMaterialNormal),
gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW)));
// auto normalAttrib = mesh->getAttributeBuffer(gpu::Stream::NORMAL);
// for (auto normal = normalAttrib.begin<glm::vec3>(); normal != normalAttrib.end<glm::vec3>(); normal++) {
// (*normal) = -(*normal);
@ -363,7 +363,7 @@ void RenderablePolyVoxEntityItem::getModel() {
// gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::RAW)));
#ifdef WANT_DEBUG
qDebug() << "---- vecIndices.size() =" << vecIndices.size();
qDebug() << "---- vecVertices.size() =" << vecVertices.size();
@ -380,22 +380,25 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
getModel();
}
glPushMatrix();
glm::mat4 m = voxelToWorldMatrix();
glMultMatrixf(&m[0][0]);
Transform transform;
transform.setTranslation(getPosition() - getRegistrationPoint() * getDimensions());
transform.setRotation(getRotation());
transform.setScale(getDimensions() / _voxelVolumeSize);
auto mesh = _modelGeometry.getMesh();
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch);
batch.setModelTransform(transform);
batch.setInputFormat(mesh->getVertexFormat());
batch.setInputBuffer(gpu::Stream::POSITION, mesh->getVertexBuffer());
batch.setInputBuffer(gpu::Stream::NORMAL,
mesh->getVertexBuffer()._buffer,
sizeof(float) * 3,
mesh->getVertexBuffer()._stride);
batch.setIndexBuffer(gpu::UINT32, mesh->getIndexBuffer()._buffer, 0);
batch.drawIndexed(gpu::TRIANGLES, mesh->getNumIndices(), 0);
auto mesh = _modelGeometry.getMesh();
gpu::Batch batch;
batch.setInputFormat(mesh->getVertexFormat());
batch.setInputBuffer(gpu::Stream::POSITION, mesh->getVertexBuffer());
batch.setInputBuffer(gpu::Stream::NORMAL,
mesh->getVertexBuffer()._buffer,
sizeof(float) * 3,
mesh->getVertexBuffer()._stride);
batch.setIndexBuffer(gpu::UINT32, mesh->getIndexBuffer()._buffer, 0);
batch.drawIndexed(gpu::TRIANGLES, mesh->getNumIndices(), 0);
gpu::GLBackend::renderBatch(batch);
glPopMatrix();
RenderableDebugableEntityItem::render(this, args);
}
@ -444,17 +447,15 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o
glm::mat4 wtvMatrix = worldToVoxelMatrix();
glm::vec3 normDirection = glm::normalize(direction);
// set ray cast length to long enough to cover all of the voxel space
float distanceToEntity = glm::distance(origin, _position);
float largestDimension = glm::max(_dimensions.x, _dimensions.y, _dimensions.z) * 2.0f;
// the PolyVox ray intersection code requires a near and far point.
// set ray cast length to long enough to cover all of the voxel space
float distanceToEntity = glm::distance(origin, getPosition());
float largestDimension = glm::max(getDimensions().x, getDimensions().y, getDimensions().z) * 2.0f;
glm::vec3 farPoint = origin + normDirection * (distanceToEntity + largestDimension);
glm::vec4 originInVoxel = wtvMatrix * glm::vec4(origin, 1.0f);
glm::vec4 farInVoxel = wtvMatrix * glm::vec4(farPoint, 1.0f);
PolyVox::Vector3DFloat startPoint(originInVoxel.x, originInVoxel.y, originInVoxel.z);
// PolyVox::Vector3DFloat pvDirection(directionInVoxel.x, directionInVoxel.y, directionInVoxel.z);
PolyVox::Vector3DFloat endPoint(farInVoxel.x, farInVoxel.y, farInVoxel.z);
PolyVox::RaycastResult raycastResult;
@ -477,7 +478,7 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o
}
result -= glm::vec4(0.5f, 0.5f, 0.5f, 0.0f);
glm::vec4 intersectedWorldPosition = voxelToWorldMatrix() * result;
distance = glm::distance(glm::vec3(intersectedWorldPosition), origin);
@ -554,9 +555,9 @@ void RenderablePolyVoxEntityItem::decompressVolumeData() {
<< voxelXSize << voxelYSize << voxelZSize;
return;
}
int rawSize = voxelXSize * voxelYSize * voxelZSize;
QByteArray compressedData;
reader >> compressedData;
QByteArray uncompressedData = qUncompress(compressedData);
@ -633,9 +634,6 @@ void RenderablePolyVoxEntityItem::computeShapeInfo(ShapeInfo& info) {
float offL = -0.5f;
float offH = 0.5f;
// float offL = 0.0f;
// float offH = 1.0f;
glm::vec3 p000 = glm::vec3(wToM * glm::vec4(x + offL, y + offL, z + offL, 1.0f));
glm::vec3 p001 = glm::vec3(wToM * glm::vec4(x + offL, y + offL, z + offH, 1.0f));
glm::vec3 p010 = glm::vec3(wToM * glm::vec4(x + offL, y + offH, z + offL, 1.0f));

View file

@ -16,6 +16,7 @@
#include "PolyVoxEntityItem.h"
#include "RenderableDebugableEntityItem.h"
#include "RenderableEntityItem.h"
class RenderablePolyVoxEntityItem : public PolyVoxEntityItem {
public:
@ -70,6 +71,8 @@ public:
virtual void setAll(uint8_t toValue);
virtual void setVoxelInVolume(glm::vec3 position, uint8_t toValue);
SIMPLE_RENDERABLE();
private:
// The PolyVoxEntityItem class has _voxelData which contains dimensions and compressed voxel data. The dimensions

View file

@ -12,6 +12,7 @@
#include <glm/gtx/quaternion.hpp>
#include <gpu/GPUConfig.h>
#include <gpu/Batch.h>
#include <DependencyManager.h>
#include <DeferredLightingEffect.h>
@ -25,35 +26,17 @@ EntityItemPointer RenderableSphereEntityItem::factory(const EntityItemID& entity
void RenderableSphereEntityItem::render(RenderArgs* args) {
PerformanceTimer perfTimer("RenderableSphereEntityItem::render");
assert(getType() == EntityTypes::Sphere);
glm::vec3 position = getPosition();
glm::vec3 center = getCenter();
glm::vec3 dimensions = getDimensions();
glm::quat rotation = getRotation();
const float MAX_COLOR = 255.0f;
glm::vec4 sphereColor(getColor()[RED_INDEX] / MAX_COLOR, getColor()[GREEN_INDEX] / MAX_COLOR,
getColor()[BLUE_INDEX] / MAX_COLOR, getLocalRenderAlpha());
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glPushMatrix();
glm::vec3 positionToCenter = center - position;
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
glScalef(dimensions.x, dimensions.y, dimensions.z);
// TODO: it would be cool to select different slices/stacks geometry based on the size of the sphere
// and the distance to the viewer. This would allow us to reduce the triangle count for smaller spheres
// that aren't close enough to see the tessellation and use larger triangle count for spheres that would
// expose that effect
const int SLICES = 15;
const int STACKS = 15;
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(0.5f, SLICES, STACKS, sphereColor);
glPopMatrix();
glPopMatrix();
Q_ASSERT(getType() == EntityTypes::Sphere);
glm::vec4 sphereColor(toGlm(getXColor()), getLocalRenderAlpha());
// TODO: it would be cool to select different slices/stacks geometry based on the size of the sphere
// and the distance to the viewer. This would allow us to reduce the triangle count for smaller spheres
// that aren't close enough to see the tessellation and use larger triangle count for spheres that would
// expose that effect
const int SLICES = 15, STACKS = 15;
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
batch.setModelTransform(getTransformToCenter());
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(batch, 0.5f, SLICES, STACKS, sphereColor);
};

View file

@ -14,6 +14,8 @@
#include <SphereEntityItem.h>
#include "RenderableEntityItem.h"
class RenderableSphereEntityItem : public SphereEntityItem {
public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
@ -23,6 +25,8 @@ public:
{ }
virtual void render(RenderArgs* args);
SIMPLE_RENDERABLE();
};

View file

@ -16,64 +16,44 @@
#include <DeferredLightingEffect.h>
#include <GeometryCache.h>
#include <PerfStat.h>
#include <TextRenderer.h>
#include "RenderableTextEntityItem.h"
#include "GLMHelpers.h"
const int FIXED_FONT_POINT_SIZE = 40;
EntityItemPointer RenderableTextEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
return EntityItemPointer(new RenderableTextEntityItem(entityID, properties));
}
void RenderableTextEntityItem::render(RenderArgs* args) {
PerformanceTimer perfTimer("RenderableTextEntityItem::render");
assert(getType() == EntityTypes::Text);
glm::vec3 position = getPosition();
Q_ASSERT(getType() == EntityTypes::Text);
static const float SLIGHTLY_BEHIND = -0.005f;
glm::vec4 textColor = glm::vec4(toGlm(getTextColorX()), 1.0f);
glm::vec4 backgroundColor = glm::vec4(toGlm(getBackgroundColorX()), 1.0f);
glm::vec3 dimensions = getDimensions();
glm::vec3 halfDimensions = dimensions / 2.0f;
glm::quat rotation = getRotation();
float leftMargin = 0.1f;
float topMargin = 0.1f;
//qCDebug(entitytree) << "RenderableTextEntityItem::render() id:" << getEntityItemID() << "text:" << getText();
glPushMatrix();
{
glTranslatef(position.x, position.y, position.z);
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
float alpha = 1.0f; //getBackgroundAlpha();
static const float SLIGHTLY_BEHIND = -0.005f;
glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, SLIGHTLY_BEHIND);
glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND);
// TODO: Determine if we want these entities to have the deferred lighting effect? I think we do, so that the color
// used for a sphere, or box have the same look as those used on a text entity.
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram();
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, glm::vec4(toGlm(getBackgroundColorX()), alpha));
DependencyManager::get<DeferredLightingEffect>()->releaseSimpleProgram();
TextRenderer* textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE / 2.0f);
glTranslatef(-(halfDimensions.x - leftMargin), halfDimensions.y - topMargin, 0.0f);
glm::vec4 textColor(toGlm(getTextColorX()), alpha);
// this is a ratio determined through experimentation
const float scaleFactor = 0.08f * _lineHeight;
glScalef(scaleFactor, -scaleFactor, scaleFactor);
glm::vec2 bounds(dimensions.x / scaleFactor, dimensions.y / scaleFactor);
textRenderer->draw(0, 0, _text, textColor, bounds);
}
glPopMatrix();
}
void RenderableTextEntityItem::enableClipPlane(GLenum plane, float x, float y, float z, float w) {
GLdouble coefficients[] = { x, y, z, w };
glClipPlane(plane, coefficients);
glEnable(plane);
Transform transformToTopLeft = getTransformToCenter();
transformToTopLeft.postTranslate(glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left
transformToTopLeft.setScale(1.0f); // Use a scale of one so that the text is not deformed
// Batch render calls
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
batch.setModelTransform(transformToTopLeft);
// Render background
glm::vec3 minCorner = glm::vec3(0.0f, -dimensions.y, SLIGHTLY_BEHIND);
glm::vec3 maxCorner = glm::vec3(dimensions.x, 0.0f, SLIGHTLY_BEHIND);
DependencyManager::get<DeferredLightingEffect>()->renderQuad(batch, minCorner, maxCorner, backgroundColor);
float scale = _lineHeight / _textRenderer->getRowHeight();
transformToTopLeft.setScale(scale); // Scale to have the correct line height
batch.setModelTransform(transformToTopLeft);
float leftMargin = 0.5f * _lineHeight, topMargin = 0.5f * _lineHeight;
glm::vec2 bounds = glm::vec2(dimensions.x - 2.0f * leftMargin, dimensions.y - 2.0f * topMargin);
_textRenderer->draw(batch, leftMargin / scale, -topMargin / scale, _text, textColor, bounds / scale);
}

View file

@ -13,6 +13,11 @@
#define hifi_RenderableTextEntityItem_h
#include <TextEntityItem.h>
#include <TextRenderer3D.h>
#include "RenderableEntityItem.h"
const int FIXED_FONT_POINT_SIZE = 40;
class RenderableTextEntityItem : public TextEntityItem {
public:
@ -21,12 +26,14 @@ public:
RenderableTextEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
TextEntityItem(entityItemID, properties)
{ }
~RenderableTextEntityItem() { delete _textRenderer; }
virtual void render(RenderArgs* args);
SIMPLE_RENDERABLE();
private:
void enableClipPlane(GLenum plane, float x, float y, float z, float w);
TextRenderer3D* _textRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE / 2.0f);
};

View file

@ -142,10 +142,10 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
glm::vec3 point = intersection.intersection;
point -= getPosition();
point = glm::inverse(getRotation()) * point;
point /= _dimensions;
point /= getDimensions();
point += 0.5f;
point.y = 1.0f - point.y;
point *= _dimensions * METERS_TO_INCHES * DPI;
point *= getDimensions() * METERS_TO_INCHES * DPI;
// Forward the mouse event.
QMouseEvent mappedEvent(event->type(),
QPoint((int)point.x, (int)point.y),
@ -161,7 +161,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
QObject::connect(renderer, &EntityTreeRenderer::mouseMoveOnEntity, forwardMouseEvent);
}
glm::vec2 dims = glm::vec2(_dimensions);
glm::vec2 dims = glm::vec2(getDimensions());
dims *= METERS_TO_INCHES * DPI;
// The offscreen surface is idempotent for resizes (bails early
// if it's a no-op), so it's safe to just call resize every frame
@ -169,43 +169,30 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
_webSurface->resize(QSize(dims.x, dims.y));
currentContext->makeCurrent(currentSurface);
Glower glow(0);
Glower glow(0.0f);
PerformanceTimer perfTimer("RenderableWebEntityItem::render");
assert(getType() == EntityTypes::Web);
glm::vec3 position = getPosition();
glm::vec3 dimensions = getDimensions();
glm::vec3 halfDimensions = dimensions / 2.0f;
glm::quat rotation = getRotation();
Q_ASSERT(getType() == EntityTypes::Web);
static const glm::vec2 texMin(0.0f);
static const glm::vec2 texMax(1.0f);
glm::vec2 topLeft(-0.5f -0.5f);
glm::vec2 bottomRight(0.5f, 0.5f);
glPushMatrix();
{
glTranslatef(position.x, position.y, position.z);
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
static const glm::vec2 texMin(0);
static const glm::vec2 texMax(1);
glm::vec2 topLeft(-halfDimensions.x, -halfDimensions.y);
glm::vec2 bottomRight(halfDimensions.x, halfDimensions.y);
if (_texture) {
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, _texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
DependencyManager::get<GeometryCache>()->renderQuad(
topLeft, bottomRight, texMin, texMax, glm::vec4(1));
if (_texture) {
glBindTexture(GL_TEXTURE_2D, 0);
glEnable(GL_TEXTURE_2D);
}
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
batch.setModelTransform(getTransformToCenter());
if (_texture) {
batch._glBindTexture(GL_TEXTURE_2D, _texture);
batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
glPopMatrix();
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, true);
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f));
DependencyManager::get<DeferredLightingEffect>()->releaseSimpleProgram(batch);
}
void RenderableWebEntityItem::setSourceUrl(const QString& value) {
qDebug() << "Setting web entity source URL to " << value;
if (_sourceUrl != value) {
qDebug() << "Setting web entity source URL to " << value;
_sourceUrl = value;
if (_webSurface) {
AbstractViewStateInterface::instance()->postLambdaEvent([this] {

View file

@ -13,6 +13,8 @@
#include <WebEntityItem.h>
#include "RenderableEntityItem.h"
class OffscreenQmlSurface;
class RenderableWebEntityItem : public WebEntityItem {
@ -25,6 +27,8 @@ public:
virtual void render(RenderArgs* args);
virtual void setSourceUrl(const QString& value);
SIMPLE_RENDERABLE();
private:
OffscreenQmlSurface* _webSurface{ nullptr };
QMetaObject::Connection _connection;

View file

@ -11,6 +11,9 @@
#include "RenderableZoneEntityItem.h"
#include <gpu/GPUConfig.h>
#include <gpu/Batch.h>
#include <DeferredLightingEffect.h>
#include <DependencyManager.h>
#include <GeometryCache.h>
@ -90,49 +93,37 @@ void RenderableZoneEntityItem::updateGeometry() {
}
void RenderableZoneEntityItem::render(RenderArgs* args) {
Q_ASSERT(getType() == EntityTypes::Zone);
if (_drawZoneBoundaries) {
switch (getShapeType()) {
case SHAPE_TYPE_COMPOUND: {
PerformanceTimer perfTimer("zone->renderCompound");
updateGeometry();
if (_model && _model->isActive()) {
PerformanceTimer perfTimer("zone->renderCompound");
glPushMatrix();
_model->renderInScene(getLocalRenderAlpha(), args);
glPopMatrix();
// FIX ME: this is no longer available... we need to switch to payloads
//_model->renderInScene(getLocalRenderAlpha(), args);
}
break;
}
case SHAPE_TYPE_BOX:
case SHAPE_TYPE_SPHERE: {
PerformanceTimer perfTimer("zone->renderPrimitive");
glm::vec3 position = getPosition();
glm::vec3 center = getCenter();
glm::vec3 dimensions = getDimensions();
glm::quat rotation = getRotation();
glm::vec4 DEFAULT_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
glm::vec4 DEFAULT_COLOR(1.0f, 1.0f, 1.0f, getLocalRenderAlpha());
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
batch.setModelTransform(getTransformToCenter());
glPushMatrix(); {
glTranslatef(position.x, position.y, position.z);
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glPushMatrix(); {
glm::vec3 positionToCenter = center - position;
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
glScalef(dimensions.x, dimensions.y, dimensions.z);
auto deferredLightingEffect = DependencyManager::get<DeferredLightingEffect>();
if (getShapeType() == SHAPE_TYPE_SPHERE) {
const int SLICES = 15;
const int STACKS = 15;
deferredLightingEffect->renderWireSphere(0.5f, SLICES, STACKS, DEFAULT_COLOR);
} else {
deferredLightingEffect->renderWireCube(1.0f, DEFAULT_COLOR);
}
} glPopMatrix();
} glPopMatrix();
auto deferredLightingEffect = DependencyManager::get<DeferredLightingEffect>();
if (getShapeType() == SHAPE_TYPE_SPHERE) {
const int SLICES = 15, STACKS = 15;
deferredLightingEffect->renderWireSphere(batch, 0.5f, SLICES, STACKS, DEFAULT_COLOR);
} else {
deferredLightingEffect->renderWireCube(batch, 1.0f, DEFAULT_COLOR);
}
break;
}
default:

View file

@ -14,3 +14,4 @@ target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${BULLET_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES})
link_hifi_libraries(avatars shared octree gpu model fbx networking animation environment)
include_hifi_library_headers(render)

View file

@ -101,8 +101,8 @@ void BoxEntityItem::debugDump() const {
quint64 now = usecTimestampNow();
qCDebug(entities) << " BOX EntityItem id:" << getEntityItemID() << "---------------------------------------------";
qCDebug(entities) << " color:" << _color[0] << "," << _color[1] << "," << _color[2];
qCDebug(entities) << " position:" << debugTreeVector(_position);
qCDebug(entities) << " dimensions:" << debugTreeVector(_dimensions);
qCDebug(entities) << " position:" << debugTreeVector(getPosition());
qCDebug(entities) << " dimensions:" << debugTreeVector(getDimensions());
qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now);
}

View file

@ -39,9 +39,9 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) :
_lastEditedFromRemoteInRemoteTime(0),
_created(UNKNOWN_CREATED_TIME),
_changedOnServer(0),
_position(ENTITY_ITEM_ZERO_VEC3),
_dimensions(ENTITY_ITEM_DEFAULT_DIMENSIONS),
_rotation(ENTITY_ITEM_DEFAULT_ROTATION),
_transform(ENTITY_ITEM_DEFAULT_ROTATION,
ENTITY_ITEM_DEFAULT_DIMENSIONS,
ENTITY_ITEM_DEFAULT_POSITION),
_glowLevel(ENTITY_ITEM_DEFAULT_GLOW_LEVEL),
_localRenderAlpha(ENTITY_ITEM_DEFAULT_LOCAL_RENDER_ALPHA),
_density(ENTITY_ITEM_DEFAULT_DENSITY),
@ -334,8 +334,8 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
}
// if this bitstream indicates that this node is the simulation owner, ignore any physics-related updates.
glm::vec3 savePosition = _position;
glm::quat saveRotation = _rotation;
glm::vec3 savePosition = getPosition();
glm::quat saveRotation = getRotation();
glm::vec3 saveVelocity = _velocity;
glm::vec3 saveAngularVelocity = _angularVelocity;
@ -613,8 +613,8 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
if (_simulatorID == myNodeID && !_simulatorID.isNull()) {
// we own the simulation, so we keep our transform+velocities and remove any related dirty flags
// rather than accept the values in the packet
_position = savePosition;
_rotation = saveRotation;
setPosition(savePosition);
setRotation(saveRotation);
_velocity = saveVelocity;
_angularVelocity = saveAngularVelocity;
_dirtyFlags &= ~(EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES);
@ -627,10 +627,11 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
}
void EntityItem::debugDump() const {
auto position = getPosition();
qCDebug(entities) << "EntityItem id:" << getEntityItemID();
qCDebug(entities, " edited ago:%f", getEditedAgo());
qCDebug(entities, " position:%f,%f,%f", _position.x, _position.y, _position.z);
qCDebug(entities) << " dimensions:" << _dimensions;
qCDebug(entities, " position:%f,%f,%f", position.x, position.y, position.z);
qCDebug(entities) << " dimensions:" << getDimensions();
}
// adjust any internal timestamps to fix clock skew for this server
@ -643,7 +644,6 @@ void EntityItem::adjustEditPacketForClockSkew(unsigned char* editPacketBuffer, s
// lastEdited
quint64 lastEditedInLocalTime;
memcpy(&lastEditedInLocalTime, dataAt, sizeof(lastEditedInLocalTime));
assert(lastEditedInLocalTime > 0);
quint64 lastEditedInServerTime = lastEditedInLocalTime + clockSkew;
memcpy(dataAt, &lastEditedInServerTime, sizeof(lastEditedInServerTime));
#ifdef WANT_DEBUG
@ -652,10 +652,11 @@ void EntityItem::adjustEditPacketForClockSkew(unsigned char* editPacketBuffer, s
qCDebug(entities) << " clockSkew: " << clockSkew;
qCDebug(entities) << " lastEditedInServerTime: " << lastEditedInServerTime;
#endif
//assert(lastEditedInLocalTime > (quint64)0);
}
float EntityItem::computeMass() const {
return _density * _volumeMultiplier * _dimensions.x * _dimensions.y * _dimensions.z;
float EntityItem::computeMass() const {
return _density * _volumeMultiplier * getDimensions().x * getDimensions().y * getDimensions().z;
}
void EntityItem::setDensity(float density) {
@ -680,8 +681,8 @@ void EntityItem::setMass(float mass) {
// Setting the mass actually changes the _density (at fixed volume), however
// we must protect the density range to help maintain stability of physics simulation
// therefore this method might not accept the mass that is supplied.
float volume = _volumeMultiplier * _dimensions.x * _dimensions.y * _dimensions.z;
float volume = _volumeMultiplier * getDimensions().x * getDimensions().y * getDimensions().z;
// compute new density
const float MIN_VOLUME = 1.0e-6f; // 0.001mm^3
@ -912,9 +913,9 @@ EntityItemProperties EntityItem::getProperties() const {
void EntityItem::getAllTerseUpdateProperties(EntityItemProperties& properties) const {
// a TerseUpdate includes the transform and its derivatives
properties._position = _position;
properties._position = getPosition();
properties._velocity = _velocity;
properties._rotation = _rotation;
properties._rotation = getRotation();
properties._angularVelocity = _angularVelocity;
properties._acceleration = _acceleration;
@ -1003,10 +1004,37 @@ void EntityItem::recordCreationTime() {
_lastSimulated = now;
}
void EntityItem::setCenterPosition(const glm::vec3& position) {
Transform transformToCenter = getTransformToCenter();
transformToCenter.setTranslation(position);
setTranformToCenter(transformToCenter);
}
// TODO: doesn't this need to handle rotation?
glm::vec3 EntityItem::getCenter() const {
return _position + (_dimensions * (glm::vec3(0.5f,0.5f,0.5f) - _registrationPoint));
const Transform EntityItem::getTransformToCenter() const {
Transform result = getTransform();
if (getRegistrationPoint() != ENTITY_ITEM_HALF_VEC3) { // If it is not already centered, translate to center
result.postTranslate(ENTITY_ITEM_HALF_VEC3 - getRegistrationPoint()); // Position to center
}
return result;
}
void EntityItem::setTranformToCenter(const Transform& transform) {
if (getRegistrationPoint() == ENTITY_ITEM_HALF_VEC3) {
// If it is already centered, just call setTransform
setTransform(transform);
return;
}
Transform copy = transform;
copy.postTranslate(getRegistrationPoint() - ENTITY_ITEM_HALF_VEC3); // Center to position
setTransform(copy);
}
void EntityItem::setDimensions(const glm::vec3& value) {
if (value.x <= 0.0f || value.y <= 0.0f || value.z <= 0.0f) {
return;
}
_transform.setScale(value);
}
/// The maximum bounding cube for the entity, independent of it's rotation.
@ -1014,13 +1042,13 @@ glm::vec3 EntityItem::getCenter() const {
///
AACube EntityItem::getMaximumAACube() const {
// * we know that the position is the center of rotation
glm::vec3 centerOfRotation = _position; // also where _registration point is
glm::vec3 centerOfRotation = getPosition(); // also where _registration point is
// * we know that the registration point is the center of rotation
// * we can calculate the length of the furthest extent from the registration point
// as the dimensions * max (registrationPoint, (1.0,1.0,1.0) - registrationPoint)
glm::vec3 registrationPoint = (_dimensions * _registrationPoint);
glm::vec3 registrationRemainder = (_dimensions * (glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint));
glm::vec3 registrationPoint = (getDimensions() * getRegistrationPoint());
glm::vec3 registrationRemainder = (getDimensions() * (glm::vec3(1.0f, 1.0f, 1.0f) - getRegistrationPoint()));
glm::vec3 furthestExtentFromRegistration = glm::max(registrationPoint, registrationRemainder);
// * we know that if you rotate in any direction you would create a sphere
@ -1042,13 +1070,13 @@ AACube EntityItem::getMinimumAACube() const {
// _position represents the position of the registration point.
glm::vec3 registrationRemainder = glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint;
glm::vec3 unrotatedMinRelativeToEntity = - (_dimensions * _registrationPoint);
glm::vec3 unrotatedMaxRelativeToEntity = _dimensions * registrationRemainder;
glm::vec3 unrotatedMinRelativeToEntity = - (getDimensions() * getRegistrationPoint());
glm::vec3 unrotatedMaxRelativeToEntity = getDimensions() * registrationRemainder;
Extents unrotatedExtentsRelativeToRegistrationPoint = { unrotatedMinRelativeToEntity, unrotatedMaxRelativeToEntity };
Extents rotatedExtentsRelativeToRegistrationPoint = unrotatedExtentsRelativeToRegistrationPoint.getRotated(getRotation());
// shift the extents to be relative to the position/registration point
rotatedExtentsRelativeToRegistrationPoint.shiftBy(_position);
rotatedExtentsRelativeToRegistrationPoint.shiftBy(getPosition());
// the cube that best encompasses extents is...
AABox box(rotatedExtentsRelativeToRegistrationPoint);
@ -1066,13 +1094,13 @@ AABox EntityItem::getAABox() const {
// _position represents the position of the registration point.
glm::vec3 registrationRemainder = glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint;
glm::vec3 unrotatedMinRelativeToEntity = - (_dimensions * _registrationPoint);
glm::vec3 unrotatedMaxRelativeToEntity = _dimensions * registrationRemainder;
glm::vec3 unrotatedMinRelativeToEntity = - (getDimensions() * _registrationPoint);
glm::vec3 unrotatedMaxRelativeToEntity = getDimensions() * registrationRemainder;
Extents unrotatedExtentsRelativeToRegistrationPoint = { unrotatedMinRelativeToEntity, unrotatedMaxRelativeToEntity };
Extents rotatedExtentsRelativeToRegistrationPoint = unrotatedExtentsRelativeToRegistrationPoint.getRotated(getRotation());
// shift the extents to be relative to the position/registration point
rotatedExtentsRelativeToRegistrationPoint.shiftBy(_position);
rotatedExtentsRelativeToRegistrationPoint.shiftBy(getPosition());
return AABox(rotatedExtentsRelativeToRegistrationPoint);
}
@ -1093,7 +1121,7 @@ AABox EntityItem::getAABox() const {
void EntityItem::setRadius(float value) {
float diameter = value * 2.0f;
float maxDimension = sqrt((diameter * diameter) / 3.0f);
_dimensions = glm::vec3(maxDimension, maxDimension, maxDimension);
setDimensions(glm::vec3(maxDimension, maxDimension, maxDimension));
}
// TODO: get rid of all users of this function...
@ -1101,7 +1129,7 @@ void EntityItem::setRadius(float value) {
// ... cornerToCornerLength = sqrt(3 x maxDimension ^ 2)
// ... radius = sqrt(3 x maxDimension ^ 2) / 2.0f;
float EntityItem::getRadius() const {
return 0.5f * glm::length(_dimensions);
return 0.5f * glm::length(getDimensions());
}
bool EntityItem::contains(const glm::vec3& point) const {
@ -1119,10 +1147,10 @@ void EntityItem::computeShapeInfo(ShapeInfo& info) {
}
void EntityItem::updatePosition(const glm::vec3& value) {
auto delta = glm::distance(_position, value);
auto delta = glm::distance(getPosition(), value);
if (delta > IGNORE_POSITION_DELTA) {
_dirtyFlags |= EntityItem::DIRTY_POSITION;
_position = value;
setPosition(value);
if (delta > ACTIVATION_POSITION_DELTA) {
_dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
}
@ -1130,9 +1158,9 @@ void EntityItem::updatePosition(const glm::vec3& value) {
}
void EntityItem::updateDimensions(const glm::vec3& value) {
auto delta = glm::distance(_dimensions, value);
auto delta = glm::distance(getDimensions(), value);
if (delta > IGNORE_DIMENSIONS_DELTA) {
_dimensions = value;
setDimensions(value);
if (delta > ACTIVATION_DIMENSIONS_DELTA) {
// rebuilding the shape will always activate
_dirtyFlags |= (EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS);
@ -1141,10 +1169,10 @@ void EntityItem::updateDimensions(const glm::vec3& value) {
}
void EntityItem::updateRotation(const glm::quat& rotation) {
if (_rotation != rotation) {
_rotation = rotation;
if (getRotation() != rotation) {
setRotation(rotation);
auto alignmentDot = glm::abs(glm::dot(_rotation, rotation));
auto alignmentDot = glm::abs(glm::dot(getRotation(), rotation));
if (alignmentDot < IGNORE_ALIGNMENT_DOT) {
_dirtyFlags |= EntityItem::DIRTY_ROTATION;
}
@ -1159,7 +1187,7 @@ void EntityItem::updateMass(float mass) {
// we must protect the density range to help maintain stability of physics simulation
// therefore this method might not accept the mass that is supplied.
float volume = _volumeMultiplier * _dimensions.x * _dimensions.y * _dimensions.z;
float volume = _volumeMultiplier * getDimensions().x * getDimensions().y * getDimensions().z;
// compute new density
float newDensity = _density;

View file

@ -12,6 +12,7 @@
#ifndef hifi_EntityItem_h
#define hifi_EntityItem_h
#include <memory>
#include <stdint.h>
#include <glm/glm.hpp>
@ -22,6 +23,7 @@
#include <OctreeElement.h> // for OctreeElement::AppendState
#include <OctreePacketData.h>
#include <ShapeInfo.h>
#include <Transform.h>
#include "EntityItemID.h"
#include "EntityItemProperties.h"
@ -33,6 +35,11 @@ class EntitySimulation;
class EntityTreeElement;
class EntityTreeElementExtraEncodeData;
namespace render {
class Scene;
class PendingChanges;
}
// these thesholds determine what updates will be ignored (client and server)
const float IGNORE_POSITION_DELTA = 0.0001f;
const float IGNORE_DIMENSIONS_DELTA = 0.0005f;
@ -151,6 +158,10 @@ public:
EntityPropertyFlags& propertyFlags, bool overwriteLocalData)
{ return 0; }
virtual bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges) { return false; } // by default entity items don't add to scene
virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges) { } // by default entity items don't add to scene
virtual void render(RenderArgs* args) { } // by default entity items don't know how to render
static int expectedBytes();
@ -176,19 +187,26 @@ public:
// attributes applicable to all entity types
EntityTypes::EntityType getType() const { return _type; }
const glm::vec3& getPosition() const { return _position; } /// get position in meters
void setPosition(const glm::vec3& value) { _position = value; }
glm::vec3 getCenter() const;
const glm::vec3& getDimensions() const { return _dimensions; } /// get dimensions in meters
/// set dimensions in meter units (0.0 - TREE_SCALE)
virtual void setDimensions(const glm::vec3& value) { _dimensions = glm::abs(value); }
const glm::quat& getRotation() const { return _rotation; }
void setRotation(const glm::quat& rotation) { _rotation = rotation; }
inline glm::vec3 getCenterPosition() const { return getTransformToCenter().getTranslation(); }
void setCenterPosition(const glm::vec3& position);
const Transform getTransformToCenter() const;
void setTranformToCenter(const Transform& transform);
inline const Transform& getTransform() const { return _transform; }
inline void setTransform(const Transform& transform) { _transform = transform; }
/// Position in meters (0.0 - TREE_SCALE)
inline const glm::vec3& getPosition() const { return _transform.getTranslation(); }
inline void setPosition(const glm::vec3& value) { _transform.setTranslation(value); }
inline const glm::quat& getRotation() const { return _transform.getRotation(); }
inline void setRotation(const glm::quat& rotation) { _transform.setRotation(rotation); }
/// Dimensions in meters (0.0 - TREE_SCALE)
inline const glm::vec3& getDimensions() const { return _transform.getScale(); }
virtual void setDimensions(const glm::vec3& value);
float getGlowLevel() const { return _glowLevel; }
void setGlowLevel(float glowLevel) { _glowLevel = glowLevel; }
@ -301,7 +319,7 @@ public:
virtual bool isReadyToComputeShape() { return true; }
virtual void computeShapeInfo(ShapeInfo& info);
virtual float getVolumeEstimate() const { return _dimensions.x * _dimensions.y * _dimensions.z; }
virtual float getVolumeEstimate() const { return getDimensions().x * getDimensions().y * getDimensions().z; }
/// return preferred shape type (actual physical shape may differ)
virtual ShapeType getShapeType() const { return SHAPE_TYPE_NONE; }
@ -368,9 +386,7 @@ protected:
quint64 _created;
quint64 _changedOnServer;
glm::vec3 _position;
glm::vec3 _dimensions;
glm::quat _rotation;
Transform _transform;
float _glowLevel;
float _localRenderAlpha;
float _density = ENTITY_ITEM_DEFAULT_DENSITY; // kg/m^3

View file

@ -18,7 +18,9 @@
// There is a minor performance gain when comparing/copying an existing glm::vec3 rather than
// creating a new one on the stack so we declare the ZERO_VEC3 constant as an optimization.
const glm::vec3 ENTITY_ITEM_ZERO_VEC3(0.0f);
const glm::vec3 ENTITY_ITEM_ZERO_VEC3 = glm::vec3(0.0f);
const glm::vec3 ENTITY_ITEM_ONE_VEC3 = glm::vec3(1.0f, 1.0f, 1.0f);
const glm::vec3 ENTITY_ITEM_HALF_VEC3 = ENTITY_ITEM_ONE_VEC3 / 2.0f;
const bool ENTITY_ITEM_DEFAULT_LOCKED = false;
const QString ENTITY_ITEM_DEFAULT_USER_DATA = QString("");
@ -31,11 +33,12 @@ const bool ENTITY_ITEM_DEFAULT_VISIBLE = true;
const QString ENTITY_ITEM_DEFAULT_SCRIPT = QString("");
const QString ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL = QString("");
const glm::vec3 ENTITY_ITEM_DEFAULT_REGISTRATION_POINT = glm::vec3(0.5f, 0.5f, 0.5f); // center
const glm::vec3 ENTITY_ITEM_DEFAULT_REGISTRATION_POINT = ENTITY_ITEM_HALF_VEC3; // center
const float ENTITY_ITEM_IMMORTAL_LIFETIME = -1.0f; /// special lifetime which means the entity lives for ever
const float ENTITY_ITEM_DEFAULT_LIFETIME = ENTITY_ITEM_IMMORTAL_LIFETIME;
const glm::vec3 ENTITY_ITEM_DEFAULT_POSITION = ENTITY_ITEM_ZERO_VEC3;
const glm::quat ENTITY_ITEM_DEFAULT_ROTATION;
const float ENTITY_ITEM_DEFAULT_WIDTH = 0.1f;
const glm::vec3 ENTITY_ITEM_DEFAULT_DIMENSIONS = glm::vec3(ENTITY_ITEM_DEFAULT_WIDTH);

View file

@ -48,10 +48,10 @@ void LightEntityItem::setDimensions(const glm::vec3& value) {
// recalculate the x/y dimensions to properly encapsulate the spotlight.
const float length = value.z;
const float width = length * glm::sin(glm::radians(_cutoff));
_dimensions = glm::vec3(width, width, length);
EntityItem::setDimensions(glm::vec3(width, width, length));
} else {
float maxDimension = glm::max(value.x, value.y, value.z);
_dimensions = glm::vec3(maxDimension, maxDimension, maxDimension);
EntityItem::setDimensions(glm::vec3(maxDimension, maxDimension, maxDimension));
}
}
@ -73,12 +73,12 @@ void LightEntityItem::setIsSpotlight(bool value) {
_isSpotlight = value;
if (_isSpotlight) {
const float length = _dimensions.z;
const float length = getDimensions().z;
const float width = length * glm::sin(glm::radians(_cutoff));
_dimensions = glm::vec3(width, width, length);
setDimensions(glm::vec3(width, width, length));
} else {
float maxDimension = glm::max(_dimensions.x, _dimensions.y, _dimensions.z);
_dimensions = glm::vec3(maxDimension, maxDimension, maxDimension);
float maxDimension = glm::max(getDimensions().x, getDimensions().y, getDimensions().z);
setDimensions(glm::vec3(maxDimension, maxDimension, maxDimension));
}
}
}
@ -89,9 +89,9 @@ void LightEntityItem::setCutoff(float value) {
if (_isSpotlight) {
// If we are a spotlight, adjusting the cutoff will affect the area we encapsulate,
// so update the dimensions to reflect this.
const float length = _dimensions.z;
const float length = getDimensions().z;
const float width = length * glm::sin(glm::radians(_cutoff));
_dimensions = glm::vec3(width, width, length);
setDimensions(glm::vec3(width, width, length));
}
}

View file

@ -86,6 +86,7 @@ bool LineEntityItem::setProperties(const EntityItemProperties& properties) {
void LineEntityItem::setLinePoints(const QVector<glm::vec3>& points) {
QVector<glm::vec3> sanitizedPoints;
int invalidPoints = 0;
for (int i = 0; i < points.size(); i++) {
glm::vec3 point = points.at(i);
// Make sure all of our points are valid numbers.
@ -93,9 +94,12 @@ void LineEntityItem::setLinePoints(const QVector<glm::vec3>& points) {
if (point.x > 0 && point.y > 0 && point.z > 0){
sanitizedPoints << point;
} else {
qDebug() << "INVALID POINT";
++invalidPoints;
}
}
if (invalidPoints > 0) {
qDebug() << "Line with" << invalidPoints << "INVALID POINTS";
}
_points = sanitizedPoints;
_pointsChanged = true;
}
@ -144,8 +148,8 @@ void LineEntityItem::debugDump() const {
quint64 now = usecTimestampNow();
qCDebug(entities) << " LINE EntityItem id:" << getEntityItemID() << "---------------------------------------------";
qCDebug(entities) << " color:" << _color[0] << "," << _color[1] << "," << _color[2];
qCDebug(entities) << " position:" << debugTreeVector(_position);
qCDebug(entities) << " dimensions:" << debugTreeVector(_dimensions);
qCDebug(entities) << " position:" << debugTreeVector(getPosition());
qCDebug(entities) << " dimensions:" << debugTreeVector(getDimensions());
qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now);
}

View file

@ -276,8 +276,8 @@ void ParticleEffectEntityItem::debugDump() const {
quint64 now = usecTimestampNow();
qCDebug(entities) << "PA EFFECT EntityItem id:" << getEntityItemID() << "---------------------------------------------";
qCDebug(entities) << " color:" << _color[0] << "," << _color[1] << "," << _color[2];
qCDebug(entities) << " position:" << debugTreeVector(_position);
qCDebug(entities) << " dimensions:" << debugTreeVector(_dimensions);
qCDebug(entities) << " position:" << debugTreeVector(getPosition());
qCDebug(entities) << " dimensions:" << debugTreeVector(getDimensions());
qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now);
}

View file

@ -145,7 +145,7 @@ void PolyVoxEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeB
void PolyVoxEntityItem::debugDump() const {
quint64 now = usecTimestampNow();
qCDebug(entities) << " POLYVOX EntityItem id:" << getEntityItemID() << "---------------------------------------------";
qCDebug(entities) << " position:" << debugTreeVector(_position);
qCDebug(entities) << " dimensions:" << debugTreeVector(_dimensions);
qCDebug(entities) << " position:" << debugTreeVector(getPosition());
qCDebug(entities) << " dimensions:" << debugTreeVector(getDimensions());
qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now);
}

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