diff --git a/examples/dialTone.js b/examples/dialTone.js new file mode 100644 index 0000000000..135acb17f4 --- /dev/null +++ b/examples/dialTone.js @@ -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); +}); diff --git a/examples/example/dynamicLandscape.js b/examples/example/dynamicLandscape.js new file mode 100644 index 0000000000..ad247963fd --- /dev/null +++ b/examples/example/dynamicLandscape.js @@ -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) + + diff --git a/examples/grab.js b/examples/grab.js index 3d95592068..306af86c68 100644 --- a/examples/grab.js +++ b/examples/grab.js @@ -1,4 +1,3 @@ - // grab.js // examples // @@ -11,247 +10,335 @@ // 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} - }); - - Controller.mouseMoveEvent.connect(mouseMoveEvent); - } -} - -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.keyPressEvent.connect(keyPressEvent); diff --git a/examples/paint.js b/examples/paint.js index 6e4438d2ea..8bba4e2571 100644 --- a/examples/paint.js +++ b/examples/paint.js @@ -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 = [{ diff --git a/examples/utilities.js b/examples/utilities.js new file mode 100644 index 0000000000..3844e23e14 --- /dev/null +++ b/examples/utilities.js @@ -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)); +} \ No newline at end of file diff --git a/examples/utilities/tools/cookies.js b/examples/utilities/tools/cookies.js index 0182bf8db0..136b5b7f82 100755 --- a/examples/utilities/tools/cookies.js +++ b/examples/utilities/tools/cookies.js @@ -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); }; diff --git a/examples/utilities/tools/renderEngineDebug.js b/examples/utilities/tools/renderEngineDebug.js new file mode 100755 index 0000000000..79b0010210 --- /dev/null +++ b/examples/utilities/tools/renderEngineDebug.js @@ -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); diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 2f91cf6a68..dffe0e0398 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -126,7 +126,7 @@ target_include_directories(${TARGET_NAME} PRIVATE ${LIBOVR_INCLUDE_DIRS}) # 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 plugins display-plugins) diff --git a/interface/resources/images/hifi-logo-blackish.svg b/interface/resources/images/hifi-logo-blackish.svg new file mode 100644 index 0000000000..60bfb3d418 --- /dev/null +++ b/interface/resources/images/hifi-logo-blackish.svg @@ -0,0 +1,123 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/interface/resources/images/login-close.svg b/interface/resources/images/login-close.svg new file mode 100644 index 0000000000..2fb10c241b --- /dev/null +++ b/interface/resources/images/login-close.svg @@ -0,0 +1,55 @@ + +image/svg+xml \ No newline at end of file diff --git a/interface/resources/images/login-password.svg b/interface/resources/images/login-password.svg new file mode 100644 index 0000000000..98d86f6d8a --- /dev/null +++ b/interface/resources/images/login-password.svg @@ -0,0 +1,58 @@ + +image/svg+xml \ No newline at end of file diff --git a/interface/resources/images/login-username.svg b/interface/resources/images/login-username.svg new file mode 100644 index 0000000000..a40dd91cf7 --- /dev/null +++ b/interface/resources/images/login-username.svg @@ -0,0 +1,51 @@ + +image/svg+xml \ No newline at end of file diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 3e03c01567..3377b20d87 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -9,18 +9,16 @@ // import Hifi 1.0 -import QtQuick 2.3 -import QtQuick.Controls 1.2 +import QtQuick 2.4 import "controls" import "styles" -Item { +DialogContainer { id: root HifiConstants { id: hifi } objectName: "AddressBarDialog" - property int animationDuration: hifi.effects.fadeInDuration property bool destroyOnInvisible: false property real scale: 1.25 // Make this dialog a little larger than normal @@ -101,57 +99,18 @@ Item { } } - // The UI enables an object, rather than manipulating its visibility, so that we can do animations in both directions. - // Because visibility and enabled are booleans, they cannot be animated. So when enabled is changed, we modify a property - // that can be animated, like scale or opacity, and then when the target animation value is reached, we can modify the - // visibility. - enabled: false - opacity: 0.0 - onEnabledChanged: { - opacity = enabled ? 1.0 : 0.0 if (enabled) { - addressLine.forceActiveFocus(); + addressLine.forceActiveFocus() } } - - onParentChanged: { - if (enabled && visible) { - addressLine.forceActiveFocus(); - } - } - - - Behavior on opacity { - // Animate opacity. - NumberAnimation { - duration: animationDuration - easing.type: Easing.OutCubic - } - } - - onOpacityChanged: { - // Once we're transparent, disable the dialog's visibility. - visible = (opacity != 0.0) - } onVisibleChanged: { if (!visible) { - reset() - - // Some dialogs should be destroyed when they become invisible. - if (destroyOnInvisible) { - destroy() - } - } else { - addressLine.forceActiveFocus(); + addressLine.text = "" } } - function reset() { - addressLine.text = "" - } - function toggleOrGo() { if (addressLine.text == "") { enabled = false @@ -160,21 +119,18 @@ Item { } } - Keys.onEscapePressed: { - enabled = false - } - Keys.onPressed: { - switch(event.key) { - case Qt.Key_W: - if (event.modifiers == Qt.ControlModifier) { - event.accepted = true - enabled = false - } + switch (event.key) { + case Qt.Key_Escape: + case Qt.Key_Back: + enabled = false + event.accepted = true + break + case Qt.Key_Enter: + case Qt.Key_Return: + toggleOrGo() + event.accepted = true break } } - - Keys.onReturnPressed: toggleOrGo() - Keys.onEnterPressed: toggleOrGo() } diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 55a0a6a461..947bf739fc 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -4,7 +4,7 @@ import QtWebKit 3.0 import "controls" import "styles" -Dialog { +VrDialog { id: root HifiConstants { id: hifi } title: "Browser" diff --git a/interface/resources/qml/ErrorDialog.qml b/interface/resources/qml/ErrorDialog.qml index c0f8132f14..76d9111d97 100644 --- a/interface/resources/qml/ErrorDialog.qml +++ b/interface/resources/qml/ErrorDialog.qml @@ -8,27 +8,22 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import Hifi 1.0 as Hifi -import QtQuick 2.3 -import QtQuick.Controls 1.2 -import QtQuick.Dialogs 1.2 +import Hifi 1.0 +import QtQuick 2.4 import "controls" import "styles" -Item { +DialogContainer { id: root HifiConstants { id: hifi } - property int animationDuration: hifi.effects.fadeInDuration - property bool destroyOnInvisible: true - Component.onCompleted: { enabled = true } onParentChanged: { if (visible && enabled) { - forceActiveFocus(); + forceActiveFocus() } } @@ -38,7 +33,7 @@ Item { x: parent ? parent.width / 2 - width / 2 : 0 y: parent ? parent.height / 2 - height / 2 : 0 - Hifi.ErrorDialog { + ErrorDialog { id: content implicitWidth: box.width @@ -69,7 +64,7 @@ Item { Text { id: messageText - font.pointSize: 10 + font.pixelSize: hifi.fonts.pixelSize * 0.6 font.weight: Font.Bold anchors { @@ -91,55 +86,17 @@ Item { } MouseArea { anchors.fill: parent + cursorShape: "PointingHandCursor" onClicked: { - content.accept(); + content.accept() } } } } } - // The UI enables an object, rather than manipulating its visibility, so that we can do animations in both directions. - // Because visibility and enabled are booleans, they cannot be animated. So when enabled is changed, we modify a property - // that can be animated, like scale or opacity, and then when the target animation value is reached, we can modify the - // visibility. - enabled: false - opacity: 0.0 - - onEnabledChanged: { - opacity = enabled ? 1.0 : 0.0 - } - - Behavior on opacity { - // Animate opacity. - NumberAnimation { - duration: animationDuration - easing.type: Easing.OutCubic - } - } - - onOpacityChanged: { - // Once we're transparent, disable the dialog's visibility. - visible = (opacity != 0.0) - } - - onVisibleChanged: { - if (!visible) { - // Some dialogs should be destroyed when they become invisible. - if (destroyOnInvisible) { - destroy() - } - } - } - Keys.onPressed: { - if (event.modifiers === Qt.ControlModifier) - switch (event.key) { - case Qt.Key_W: - event.accepted = true - content.accept() - break - } else switch (event.key) { + switch (event.key) { case Qt.Key_Escape: case Qt.Key_Back: case Qt.Key_Enter: diff --git a/interface/resources/qml/InfoView.qml b/interface/resources/qml/InfoView.qml index 6b49e6f0c7..012f04f1fd 100644 --- a/interface/resources/qml/InfoView.qml +++ b/interface/resources/qml/InfoView.qml @@ -5,7 +5,7 @@ import QtQuick.Controls.Styles 1.3 import QtWebKit 3.0 import "controls" -Dialog { +VrDialog { id: root width: 800 height: 800 diff --git a/interface/resources/qml/LoginDialog.qml b/interface/resources/qml/LoginDialog.qml index 5653dfc7a1..8d5267f7f8 100644 --- a/interface/resources/qml/LoginDialog.qml +++ b/interface/resources/qml/LoginDialog.qml @@ -1,78 +1,173 @@ +// +// LoginDialog.qml +// +// Created by David Rowe on 3 Jun 2015 +// 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 +// + import Hifi 1.0 -import QtQuick 2.3 -import QtQuick.Controls.Styles 1.3 +import QtQuick 2.4 import "controls" import "styles" -Dialog { +DialogContainer { + id: root HifiConstants { id: hifi } - title: "Login" + objectName: "LoginDialog" - height: 512 - width: 384 - onVisibleChanged: { - if (!visible) { - reset() - } - } + property bool destroyOnInvisible: false - onEnabledChanged: { - if (enabled) { - username.forceActiveFocus(); - } - } + implicitWidth: loginDialog.implicitWidth + implicitHeight: loginDialog.implicitHeight - function reset() { - username.text = "" - password.text = "" - loginDialog.statusText = "" - } + x: parent ? parent.width / 2 - width / 2 : 0 + y: parent ? parent.height / 2 - height / 2 : 0 + property int maximumX: parent ? parent.width - width : 0 + property int maximumY: parent ? parent.height - height : 0 LoginDialog { id: loginDialog - anchors.fill: parent - anchors.margins: parent.margins - anchors.topMargin: parent.topMargin - Column { - anchors.topMargin: 8 - anchors.right: parent.right - anchors.rightMargin: 0 - anchors.left: parent.left - anchors.top: parent.top - spacing: 8 - Image { - height: 64 - anchors.horizontalCenter: parent.horizontalCenter - width: 64 - source: "../images/hifi-logo.svg" + implicitWidth: backgroundRectangle.width + implicitHeight: backgroundRectangle.height + + readonly property int inputWidth: 500 + readonly property int inputHeight: 60 + readonly property int borderWidth: 30 + readonly property int closeMargin: 16 + readonly property real tan30: 0.577 // tan(30°) + readonly property int inputSpacing: 16 + property int maximumX: parent ? parent.width - width : 0 + property int maximumY: parent ? parent.height - height : 0 + + Rectangle { + id: backgroundRectangle + width: loginDialog.inputWidth + loginDialog.borderWidth * 2 + height: loginDialog.inputHeight * 6 + loginDialog.closeMargin * 2 + radius: loginDialog.closeMargin * 2 + + color: "#2c86b1" + opacity: 0.85 + + MouseArea { + width: parent.width + height: parent.height + anchors { + horizontalCenter: parent.horizontalCenter + verticalCenter: parent.verticalCenter + } + drag { + target: root + minimumX: 0 + minimumY: 0 + maximumX: root.parent ? root.maximumX : 0 + maximumY: root.parent ? root.maximumY : 0 + } + } + } + + Image { + id: closeIcon + source: "../images/login-close.svg" + width: 20 + height: 20 + anchors { + top: backgroundRectangle.top + right: backgroundRectangle.right + topMargin: loginDialog.closeMargin + rightMargin: loginDialog.closeMargin } - Border { - width: 304 - height: 64 - anchors.horizontalCenter: parent.horizontalCenter + MouseArea { + anchors.fill: parent + cursorShape: "PointingHandCursor" + onClicked: { + root.enabled = false + } + } + } + + Column { + id: mainContent + width: loginDialog.inputWidth + spacing: loginDialog.inputSpacing + anchors { + horizontalCenter: parent.horizontalCenter + verticalCenter: parent.verticalCenter + } + + Item { + // Offset content down a little + width: loginDialog.inputWidth + height: loginDialog.closeMargin + } + + Rectangle { + width: loginDialog.inputWidth + height: loginDialog.inputHeight + radius: height / 2 + color: "#ebebeb" + + Image { + source: "../images/login-username.svg" + width: loginDialog.inputHeight * 0.65 + height: width + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + leftMargin: loginDialog.inputHeight / 4 + } + } + TextInput { id: username - anchors.fill: parent - helperText: "Username or Email" - anchors.margins: 8 + anchors { + fill: parent + leftMargin: loginDialog.inputHeight + rightMargin: loginDialog.inputHeight / 2 + } + + helperText: "username or email" + color: hifi.colors.text + KeyNavigation.tab: password KeyNavigation.backtab: password } } - Border { - width: 304 - height: 64 - anchors.horizontalCenter: parent.horizontalCenter + Rectangle { + width: loginDialog.inputWidth + height: loginDialog.inputHeight + radius: height / 2 + color: "#ebebeb" + + Image { + source: "../images/login-password.svg" + width: loginDialog.inputHeight * 0.65 + height: width + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + leftMargin: loginDialog.inputHeight / 4 + } + } + TextInput { id: password - anchors.fill: parent + anchors { + fill: parent + leftMargin: loginDialog.inputHeight + rightMargin: loginDialog.inputHeight / 2 + } + + helperText: "password" echoMode: TextInput.Password - helperText: "Password" - anchors.margins: 8 + color: hifi.colors.text + KeyNavigation.tab: username KeyNavigation.backtab: username onFocusChanged: { @@ -83,102 +178,176 @@ Dialog { } } - Text { - anchors.horizontalCenter: parent.horizontalCenter - textFormat: Text.StyledText - width: parent.width - height: 96 - wrapMode: Text.WordWrap - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - text: loginDialog.statusText - } - } + Item { + width: loginDialog.inputWidth + height: loginDialog.inputHeight / 2 - Column { - anchors.bottomMargin: 5 - anchors.right: parent.right - anchors.rightMargin: 0 - anchors.left: parent.left - anchors.bottom: parent.bottom + Text { + id: messageText + + visible: loginDialog.statusText != "" && loginDialog.statusText != "Logging in..." + + width: loginDialog.inputWidth + height: loginDialog.inputHeight / 2 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + text: loginDialog.statusText + color: "white" + } + + Row { + id: messageSpinner + + visible: loginDialog.statusText == "Logging in..." + onVisibleChanged: visible ? messageSpinnerAnimation.restart() : messageSpinnerAnimation.stop() + + spacing: 24 + anchors { + verticalCenter: parent.verticalCenter + horizontalCenter: parent.horizontalCenter + } + + Rectangle { + id: spinner1 + width: 10 + height: 10 + color: "#ebebeb" + opacity: 0.05 + } + + Rectangle { + id: spinner2 + width: 10 + height: 10 + color: "#ebebeb" + opacity: 0.05 + } + + Rectangle { + id: spinner3 + width: 10 + height: 10 + color: "#ebebeb" + opacity: 0.05 + } + + SequentialAnimation { + id: messageSpinnerAnimation + running: messageSpinner.visible + loops: Animation.Infinite + NumberAnimation { target: spinner1; property: "opacity"; to: 1.0; duration: 1000 } + NumberAnimation { target: spinner2; property: "opacity"; to: 1.0; duration: 1000 } + NumberAnimation { target: spinner3; property: "opacity"; to: 1.0; duration: 1000 } + NumberAnimation { target: spinner1; property: "opacity"; to: 0.05; duration: 0 } + NumberAnimation { target: spinner2; property: "opacity"; to: 0.05; duration: 0 } + NumberAnimation { target: spinner3; property: "opacity"; to: 0.05; duration: 0 } + } + } + } Rectangle { - width: 192 - height: 64 - anchors.horizontalCenter: parent.horizontalCenter - color: hifi.colors.hifiBlue - border.width: 0 - radius: 10 + width: loginDialog.inputWidth + height: loginDialog.inputHeight + radius: height / 2 + color: "#353535" + + TextInput { + anchors.fill: parent + text: "Login" + color: "white" + horizontalAlignment: Text.AlignHCenter + } MouseArea { - anchors.bottom: parent.bottom - anchors.bottomMargin: 0 - anchors.top: parent.top - anchors.right: parent.right - anchors.left: parent.left + anchors.fill: parent + cursorShape: "PointingHandCursor" onClicked: { loginDialog.login(username.text, password.text) } } + } - Row { - anchors.centerIn: parent + Row { + anchors.horizontalCenter: parent.horizontalCenter + + Text { + text: "Password?" + font.pixelSize: hifi.fonts.pixelSize * 0.8 + font.underline: true + color: "#e0e0e0" + width: loginDialog.inputHeight * 4 + horizontalAlignment: Text.AlignRight anchors.verticalCenter: parent.verticalCenter - spacing: 8 + + MouseArea { + anchors.fill: parent + cursorShape: "PointingHandCursor" + onClicked: { + loginDialog.openUrl(loginDialog.rootUrl + "/users/password/new") + } + } + } + + Item { + width: loginDialog.inputHeight + loginDialog.inputSpacing * 2 + height: loginDialog.inputHeight + Image { - id: loginIcon - height: 32 - width: 32 - source: "../images/login.svg" - } - Text { - text: "Login" - color: "white" - width: 64 - height: parent.height + id: hifiIcon + source: "../images/hifi-logo-blackish.svg" + width: loginDialog.inputHeight + height: width + anchors { + horizontalCenter: parent.horizontalCenter + verticalCenter: parent.verticalCenter + } } } - } + Text { + text: "Register" + font.pixelSize: hifi.fonts.pixelSize * 0.8 + font.underline: true + color: "#e0e0e0" + width: loginDialog.inputHeight * 4 + horizontalAlignment: Text.AlignLeft + anchors.verticalCenter: parent.verticalCenter - Text { - width: parent.width - height: 24 - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - text:"Create Account" - font.pointSize: 12 - font.bold: true - color: hifi.colors.hifiBlue - - MouseArea { - anchors.fill: parent - onClicked: { - loginDialog.openUrl(loginDialog.rootUrl + "/signup") - } - } - } - - Text { - width: parent.width - height: 24 - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - font.pointSize: 12 - text: "Recover Password" - color: hifi.colors.hifiBlue - - MouseArea { - anchors.fill: parent - onClicked: { - loginDialog.openUrl(loginDialog.rootUrl + "/users/password/new") + MouseArea { + anchors.fill: parent + cursorShape: "PointingHandCursor" + onClicked: { + loginDialog.openUrl(loginDialog.rootUrl + "/signup") + } } } } } } + + onOpacityChanged: { + // Set focus once animation is completed so that focus is set at start-up when not logged in + if (opacity == 1.0) { + username.forceActiveFocus() + } + } + + onVisibleChanged: { + if (!visible) { + username.text = "" + password.text = "" + loginDialog.statusText = "" + } + } + Keys.onPressed: { - switch(event.key) { + switch (event.key) { + case Qt.Key_Escape: + case Qt.Key_Back: + enabled = false + event.accepted = true + break case Qt.Key_Enter: case Qt.Key_Return: if (username.activeFocus) { @@ -192,7 +361,7 @@ Dialog { loginDialog.login(username.text, password.text) } } - break; + break } } } diff --git a/interface/resources/qml/MarketplaceDialog.qml b/interface/resources/qml/MarketplaceDialog.qml index 58bb3e6183..946f32e84a 100644 --- a/interface/resources/qml/MarketplaceDialog.qml +++ b/interface/resources/qml/MarketplaceDialog.qml @@ -5,7 +5,7 @@ import QtQuick.Controls.Styles 1.3 import QtWebKit 3.0 import "controls" -Dialog { +VrDialog { title: "Test Dlg" id: testDialog objectName: "Browser" diff --git a/interface/resources/qml/MessageDialog.qml b/interface/resources/qml/MessageDialog.qml index dd410b8070..e8b01df9d0 100644 --- a/interface/resources/qml/MessageDialog.qml +++ b/interface/resources/qml/MessageDialog.qml @@ -5,7 +5,7 @@ import QtQuick.Dialogs 1.2 import "controls" import "styles" -Dialog { +VrDialog { id: root HifiConstants { id: hifi } property real spacing: hifi.layout.spacing diff --git a/interface/resources/qml/TestDialog.qml b/interface/resources/qml/TestDialog.qml index 15bd790c22..e6675b7282 100644 --- a/interface/resources/qml/TestDialog.qml +++ b/interface/resources/qml/TestDialog.qml @@ -3,7 +3,7 @@ import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.3 import "controls" -Dialog { +VrDialog { title: "Test Dialog" id: testDialog objectName: "TestDialog" diff --git a/interface/resources/qml/controls/DialogContainer.qml b/interface/resources/qml/controls/DialogContainer.qml new file mode 100644 index 0000000000..4aa4e45d67 --- /dev/null +++ b/interface/resources/qml/controls/DialogContainer.qml @@ -0,0 +1,65 @@ +// +// DialogCommon.qml +// +// Created by David Rowe on 3 Jun 2015 +// 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 +// + +import Hifi 1.0 +import QtQuick 2.4 +import "../styles" + +Item { + id: root + + property bool destroyOnInvisible: true + + + // The UI enables an object, rather than manipulating its visibility, so that we can do animations in both directions. + // Because visibility and enabled are booleans, they cannot be animated. So when enabled is changed, we modify a property + // that can be animated, like scale or opacity, and then when the target animation value is reached, we can modify the + // visibility. + enabled: false + opacity: 0.0 + + onEnabledChanged: { + opacity = enabled ? 1.0 : 0.0 + } + + Behavior on opacity { + // Animate opacity. + NumberAnimation { + duration: hifi.effects.fadeInDuration + easing.type: Easing.OutCubic + } + } + + onOpacityChanged: { + // Once we're transparent, disable the dialog's visibility. + visible = (opacity != 0.0) + } + + onVisibleChanged: { + if (!visible) { + // Some dialogs should be destroyed when they become invisible. + if (destroyOnInvisible) { + destroy() + } + } + } + + + Keys.onPressed: { + switch(event.key) { + case Qt.Key_W: + if (event.modifiers == Qt.ControlModifier) { + enabled = false + event.accepted = true + } + break + } + } +} diff --git a/interface/resources/qml/controls/Dialog.qml b/interface/resources/qml/controls/VrDialog.qml similarity index 100% rename from interface/resources/qml/controls/Dialog.qml rename to interface/resources/qml/controls/VrDialog.qml diff --git a/interface/resources/qml/styles/HifiConstants.qml b/interface/resources/qml/styles/HifiConstants.qml index fa556f2083..c232b993d1 100644 --- a/interface/resources/qml/styles/HifiConstants.qml +++ b/interface/resources/qml/styles/HifiConstants.qml @@ -13,10 +13,9 @@ Item { readonly property color hifiBlue: "#0e7077" readonly property color window: sysPalette.window readonly property color dialogBackground: sysPalette.window - //readonly property color dialogBackground: "#00000000" readonly property color inputBackground: "white" readonly property color background: sysPalette.dark - readonly property color text: sysPalette.text + readonly property color text: "#202020" readonly property color disabledText: "gray" readonly property color hintText: "gray" // A bit darker than sysPalette.dark so that it is visible on the DK2 readonly property color light: sysPalette.light diff --git a/interface/resources/sounds/short1.wav b/interface/resources/sounds/short1.wav new file mode 100644 index 0000000000..fb03f5dd49 Binary files /dev/null and b/interface/resources/sounds/short1.wav differ diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7effc93f07..449264fa0d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -61,9 +61,14 @@ #include #include #include +#include #include #include #include +#include +#include +#include +#include #include #include #include @@ -102,7 +107,6 @@ #include "Util.h" #include "InterfaceLogging.h" -#include "GlWindow.h" #include "avatar/AvatarManager.h" #include "audio/AudioToolBox.h" @@ -116,11 +120,7 @@ #include "devices/RealSense.h" #include "devices/MIDIManager.h" -#include "gpu/Batch.h" -#include "gpu/GLBackend.h" - -#include - +#include "RenderDeferredTask.h" #include "scripting/AccountScriptingInterface.h" #include "scripting/AudioDeviceScriptingInterface.h" #include "scripting/ClipboardScriptingInterface.h" @@ -145,6 +145,7 @@ #include "ui/Stats.h" #include "ui/AddressBarDialog.h" + // ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU #if defined(Q_OS_WIN) extern "C" { @@ -252,12 +253,12 @@ bool setupEssentials(int& argc, char** argv) { } // Set build version QCoreApplication::setApplicationVersion(BUILD_VERSION); - + DependencyManager::registerInheritance(); DependencyManager::registerInheritance(); Setting::init(); - + // Set dependencies auto addressManager = DependencyManager::set(); auto nodeList = DependencyManager::set(NodeType::Agent, listenPort); @@ -291,6 +292,7 @@ bool setupEssentials(int& argc, char** argv) { auto discoverabilityManager = DependencyManager::set(); auto sceneScriptingInterface = DependencyManager::set(); auto offscreenUi = DependencyManager::set(); + auto pathUtils = DependencyManager::set(); return true; } @@ -330,7 +332,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _enableProcessOctreeThread(true), _octreeProcessor(), _nodeBoundsDisplay(this), - _applicationOverlay(), _runningScriptsWidget(NULL), _runningScriptsWidgetWasVisible(false), _trayIcon(new QSystemTrayIcon(_window)), @@ -341,7 +342,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _notifiedPacketVersionMismatchThisDomain(false), _domainConnectionRefusals(QList()), _maxOctreePPS(maxOctreePacketsPerSecond.get()), - _lastFaceTrackerUpdate(0) + _lastFaceTrackerUpdate(0), + _applicationOverlay() { _offscreenContext = new OffscreenGlCanvas(); setInstance(this); @@ -355,9 +357,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "styles/Inconsolata.otf"); _window->setWindowTitle("Interface"); - + Model::setAbstractViewStateInterface(this); // The model class will sometimes need to know view state details from us - + auto nodeList = DependencyManager::get(); _myAvatar = DependencyManager::get()->getMyAvatar(); @@ -369,7 +371,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _bookmarks = new Bookmarks(); // Before setting up the menu _runningScriptsWidget = new RunningScriptsWidget(_window); - + _renderEngine->addTask(render::TaskPointer(new RenderDeferredTask())); + _renderEngine->registerScene(_main3DScene); + // start the nodeThread so its event loop is running QThread* nodeThread = new QThread(this); nodeThread->setObjectName("Datagram Processor Thread"); @@ -377,14 +381,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // make sure the node thread is given highest priority nodeThread->setPriority(QThread::TimeCriticalPriority); - + _datagramProcessor = new DatagramProcessor(nodeList.data()); - + // have the NodeList use deleteLater from DM customDeleter nodeList->setCustomDeleter([](Dependency* dependency) { static_cast(dependency)->deleteLater(); }); - + // put the NodeList and datagram processing on the node thread nodeList->moveToThread(nodeThread); @@ -392,7 +396,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // emit checkBackgroundDownloads to cause the GeometryCache to check it's queue for requested background // downloads. QSharedPointer geometryCacheP = DependencyManager::get(); - ResourceCache *geometryCache = geometryCacheP.data(); + ResourceCache* geometryCache = geometryCacheP.data(); connect(this, &Application::checkBackgroundDownloads, geometryCache, &ResourceCache::checkAsynchronousGets); // connect the DataProcessor processDatagrams slot to the QUDPSocket readyRead() signal @@ -401,20 +405,22 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // put the audio processing on a separate thread QThread* audioThread = new QThread(); audioThread->setObjectName("Audio Thread"); - + auto audioIO = DependencyManager::get(); - + audioIO->setPositionGetter(getPositionForAudio); audioIO->setOrientationGetter(getOrientationForAudio); - + audioIO->moveToThread(audioThread); connect(audioThread, &QThread::started, audioIO.data(), &AudioClient::start); connect(audioIO.data(), &AudioClient::destroyed, audioThread, &QThread::quit); connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater); connect(audioIO.data(), &AudioClient::muteToggled, this, &Application::audioMuteToggled); + connect(audioIO.data(), &AudioClient::receivedFirstPacket, + &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::receivedFirstPacket); audioThread->start(); - + const DomainHandler& domainHandler = nodeList->getDomainHandler(); connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&))); @@ -433,7 +439,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : auto discoverabilityManager = DependencyManager::get(); connect(locationUpdateTimer, &QTimer::timeout, discoverabilityManager.data(), &DiscoverabilityManager::updateLocation); locationUpdateTimer->start(DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS); - + // if we get a domain change, immediately attempt update location in metaverse server connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain, discoverabilityManager.data(), &DiscoverabilityManager::updateLocation); @@ -467,14 +473,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // once the event loop has started, check and signal for an access token QMetaObject::invokeMethod(&accountManager, "checkAndSignalForAccessToken", Qt::QueuedConnection); - + auto addressManager = DependencyManager::get(); - + // use our MyAvatar position and quat for address manager path addressManager->setPositionGetter(getPositionForPath); addressManager->setOrientationGetter(getOrientationForPath); - - connect(addressManager.data(), &AddressManager::rootPlaceNameChanged, this, &Application::updateWindowTitle); + + connect(addressManager.data(), &AddressManager::hostChanged, this, &Application::updateWindowTitle); connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress); #ifdef _WIN32 @@ -505,7 +511,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE); cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache"); networkAccessManager.setCache(cache); - + ResourceCache::setRequestLimit(3); _offscreenContext->create(); @@ -518,10 +524,16 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _window->setCentralWidget(container); _window->restoreGeometry(); _window->setVisible(true); +<<<<<<< HEAD container->setFocus(); _offscreenContext->makeCurrent(); initializeGL(); // initialization continues in initializeGL when OpenGL context is ready +======= + _glWidget->setFocusPolicy(Qt::StrongFocus); + _glWidget->setFocus(); + _glWidget->setCursor(Qt::BlankCursor); +>>>>>>> master _menuBarHeight = Menu::getInstance()->height(); @@ -567,11 +579,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(nodeList.data(), SIGNAL(dataReceived(const quint8, const int)), bandwidthRecorder.data(), SLOT(updateInboundData(const quint8, const int))); - connect(&_myAvatar->getSkeletonModel(), &SkeletonModel::skeletonLoaded, + connect(&_myAvatar->getSkeletonModel(), &SkeletonModel::skeletonLoaded, this, &Application::checkSkeleton, Qt::QueuedConnection); // Setup the userInputMapper with the actions - // Setup the keyboardMouseDevice and the user input mapper with the default bindings + // Setup the keyboardMouseDevice and the user input mapper with the default bindings _keyboardMouseDevice.registerToUserInputMapper(_userInputMapper); _keyboardMouseDevice.assignDefaultInputMapping(_userInputMapper); @@ -587,7 +599,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // do this as late as possible so that all required subsystems are initialized loadScripts(); } - + loadSettings(); int SAVE_SETTINGS_INTERVAL = 10 * MSECS_PER_SECOND; // Let's save every seconds for now connect(&_settingsTimer, &QTimer::timeout, this, &Application::saveSettings); @@ -597,12 +609,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _settingsTimer.setSingleShot(false); _settingsTimer.setInterval(SAVE_SETTINGS_INTERVAL); _settingsThread.start(); - + _trayIcon->show(); - + // set the local loopback interface for local sounds from audio scripts AudioScriptingInterface::getInstance().setLocalAudioInterface(audioIO.data()); - + #ifdef HAVE_RTMIDI // setup the MIDIManager MIDIManager& midiManagerInstance = MIDIManager::getInstance(); @@ -610,6 +622,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : #endif this->installEventFilter(this); +<<<<<<< HEAD +======= + // The offscreen UI needs to intercept the mouse and keyboard + // events coming from the onscreen window + _glWidget->installEventFilter(DependencyManager::get().data()); +>>>>>>> master // initialize our face trackers after loading the menu settings auto faceshiftTracker = DependencyManager::get(); @@ -625,7 +643,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : void Application::aboutToQuit() { emit beforeAboutToQuit(); - + _aboutToQuit = true; cleanupBeforeQuit(); } @@ -637,7 +655,7 @@ void Application::cleanupBeforeQuit() { _datagramProcessor->shutdown(); // tell the datagram processor we're shutting down, so it can short circuit _entities.shutdown(); // tell the entities system we're shutting down, so it will stop running scripts ScriptEngine::stopAllScripts(this); // stop all currently running global scripts - + // first stop all timers directly or by invokeMethod // depending on what thread they run in locationUpdateTimer->stop(); @@ -664,11 +682,11 @@ void Application::cleanupBeforeQuit() { // let the avatar mixer know we're out MyAvatar::sendKillAvatar(); - + // stop the AudioClient QMetaObject::invokeMethod(DependencyManager::get().data(), "stop", Qt::BlockingQueuedConnection); - + // destroy the AudioClient so it and its thread have a chance to go down safely DependencyManager::destroy(); @@ -677,12 +695,12 @@ void Application::cleanupBeforeQuit() { #endif } -Application::~Application() { +Application::~Application() { EntityTree* tree = _entities.getTree(); tree->lockForWrite(); _entities.getTree()->setSimulation(NULL); tree->unlock(); - + _octreeProcessor.terminate(); _entityEditSender.terminate(); @@ -693,7 +711,12 @@ Application::~Application() { ModelEntityItem::cleanupLoadedAnimations(); +<<<<<<< HEAD getActiveDisplayPlugin()->deactivate(); +======= + // stop the glWidget frame timer so it doesn't call paintGL + _glWidget->stopFrameTimer(); +>>>>>>> master // remove avatars from physics engine DependencyManager::get()->clearOtherAvatars(); @@ -706,10 +729,10 @@ Application::~Application() { DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); - + QThread* nodeThread = DependencyManager::get()->thread(); DependencyManager::destroy(); - + // ask the node thread to quit and wait until it is done nodeThread->quit(); nodeThread->wait(); @@ -763,8 +786,8 @@ void Application::initializeGL() { initDisplay(); qCDebug(interfaceapp, "Initialized Display."); - // The UI can't be created until the primary OpenGL - // context is created, because it needs to share + // The UI can't be created until the primary OpenGL + // context is created, because it needs to share // texture resources initializeUi(); qCDebug(interfaceapp, "Initialized Offscreen UI."); @@ -839,8 +862,19 @@ void Application::paintGL() { return; } PROFILE_RANGE(__FUNCTION__); +<<<<<<< HEAD auto displayPlugin = getActiveDisplayPlugin(); displayPlugin->preRender(); +======= + _glWidget->makeCurrent(); + + auto lodManager = DependencyManager::get(); + gpu::Context context(new gpu::GLBackend()); + RenderArgs renderArgs(&context, nullptr, getViewFrustum(), lodManager->getOctreeSizeScale(), + lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE, + RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); + +>>>>>>> master PerformanceTimer perfTimer("paintGL"); _offscreenContext->makeCurrent(); @@ -851,7 +885,7 @@ void Application::paintGL() { { PerformanceTimer perfTimer("renderOverlay"); - _applicationOverlay.renderOverlay(); + _applicationOverlay.renderOverlay(&renderArgs); } glEnable(GL_LINE_SMOOTH); @@ -901,11 +935,12 @@ void Application::paintGL() { _myCamera.loadViewFrustum(_viewFrustum); if (getShadowsEnabled()) { - updateShadowMap(); + renderArgs._renderMode = RenderArgs::SHADOW_RENDER_MODE; + updateShadowMap(&renderArgs); } + renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; DependencyManager::get()->prepare(); - // Primary rendering pass auto primaryFbo = DependencyManager::get()->getPrimaryFramebuffer(); auto finalFbo = primaryFbo; @@ -956,6 +991,26 @@ void Application::paintGL() { } glPopMatrix(); +#if 0 + if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { + _rearMirrorTools->render(&renderArgs, true, _glWidget->mapFromGlobal(QCursor::pos())); + } else if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { + renderRearViewMirror(&renderArgs, _mirrorViewRect); + } + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBindFramebuffer(GL_READ_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(finalFbo)); + glBlitFramebuffer(0, 0, _renderResolution.x, _renderResolution.y, + 0, 0, _glWidget->getDeviceSize().width(), _glWidget->getDeviceSize().height(), + GL_COLOR_BUFFER_BIT, GL_NEAREST); + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + + _applicationOverlay.displayOverlayTexture(); + } +if (!OculusManager::isConnected() || OculusManager::allowSwap()) { + _glWidget->swapBuffers(); +} +#endif // This might not be needed *right now*. We want to ensure that the FBO rendering // has completed before we start trying to read from it in another context. However @@ -975,6 +1030,7 @@ void Application::paintGL() { Q_ASSERT(!QOpenGLContext::currentContext()); _offscreenContext->makeCurrent(); _frameCount++; + Stats::getInstance()->setRenderDetails(renderArgs._details); } void Application::runTests() { @@ -1036,7 +1092,6 @@ void Application::resizeGL() { int horizontalOffset = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2; Stats::getInstance()->resetWidth(uiSize.x, horizontalOffset); } - } void Application::controlledBroadcastToNodes(const QByteArray& packet, const NodeSet& destinationNodeTypes) { @@ -1102,11 +1157,11 @@ bool Application::event(QEvent* event) { // handle custom URL if (event->type() == QEvent::FileOpen) { - + QFileOpenEvent* fileEvent = static_cast(event); QUrl url = fileEvent->url(); - + if (!url.isEmpty()) { QString urlString = url.toString(); if (canAcceptURL(urlString)) { @@ -1115,7 +1170,7 @@ bool Application::event(QEvent* event) { } return false; } - + if (HFActionEvent::types().contains(event->type())) { _controllerScriptingInterface.handleMetaEvent(static_cast(event)); } @@ -1180,7 +1235,7 @@ void Application::keyPressEvent(QKeyEvent* event) { Menu::getInstance()->triggerOption(MenuOption::AddressBar); } else if (isShifted) { Menu::getInstance()->triggerOption(MenuOption::LodTools); - } + } break; case Qt::Key_F: { @@ -1221,7 +1276,7 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_Backslash: Menu::getInstance()->triggerOption(MenuOption::Chat); break; - + case Qt::Key_Up: if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { if (!isShifted) { @@ -1353,7 +1408,7 @@ void Application::keyPressEvent(QKeyEvent* event) { computePickRay(getTrueMouseX(), getTrueMouseY())); sendEvent(this, &startActionEvent); } - + break; } case Qt::Key_Escape: { @@ -1363,7 +1418,7 @@ void Application::keyPressEvent(QKeyEvent* event) { HFBackEvent startBackEvent(HFBackEvent::startType()); sendEvent(this, &startBackEvent); } - + break; } @@ -1420,7 +1475,7 @@ void Application::keyReleaseEvent(QKeyEvent* event) { void Application::focusOutEvent(QFocusEvent* event) { _keyboardMouseDevice.focusOutEvent(event); - + // synthesize events for keys currently pressed, since we may not get their release events foreach (int key, _keysPressed) { QKeyEvent event(QEvent::KeyRelease, key, Qt::NoModifier); @@ -1454,7 +1509,7 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { #endif _entities.mouseMoveEvent(event, deviceID); - + _controllerScriptingInterface.emitMouseMoveEvent(event, deviceID); // send events to any registered scripts // if one of our scripts have asked to capture this event, then stop processing it if (_controllerScriptingInterface.isMouseCaptured()) { @@ -1462,7 +1517,7 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { } _keyboardMouseDevice.mouseMoveEvent(event, deviceID); - + } void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { @@ -1487,7 +1542,7 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { if (event->button() == Qt::LeftButton) { _mouseDragStarted = getTrueMousePosition(); _mousePressed = true; - + if (mouseOnScreen()) { if (DependencyManager::get()->mousePressEvent(getMouseX(), getMouseY())) { // stop propagation @@ -1504,7 +1559,7 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { return; } } - + // nobody handled this - make it an action event on the _window object HFActionEvent actionEvent(HFActionEvent::startType(), computePickRay(event->x(), event->y())); @@ -1552,14 +1607,14 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { if (event->button() == Qt::LeftButton) { _mousePressed = false; - + if (Menu::getInstance()->isOptionChecked(MenuOption::Stats) && mouseOnScreen()) { // let's set horizontal offset to give stats some margin to mirror int horizontalOffset = MIRROR_VIEW_WIDTH; Stats::getInstance()->checkClick(getMouseX(), getMouseY(), getMouseDragStartedX(), getMouseDragStartedY(), horizontalOffset); } - + // fire an action end event HFActionEvent actionEvent(HFActionEvent::endType(), computePickRay(event->x(), event->y())); @@ -1663,7 +1718,7 @@ void Application::dropEvent(QDropEvent *event) { } } } - + if (atLeastOneFileAccepted) { event->acceptProposedAction(); } @@ -1683,7 +1738,7 @@ void Application::dragEnterEvent(QDragEnterEvent* event) { bool Application::acceptSnapshot(const QString& urlString) { QUrl url(urlString); QString snapshotPath = url.toLocalFile(); - + SnapshotMetaData* snapshotData = Snapshot::parseSnapshotData(snapshotPath); if (snapshotData) { if (!snapshotData->getURL().toString().isEmpty()) { @@ -1693,7 +1748,7 @@ bool Application::acceptSnapshot(const QString& urlString) { QMessageBox msgBox; msgBox.setText("No location details were found in the file " + snapshotPath + ", try dragging in an authentic Hifi snapshot."); - + msgBox.setStandardButtons(QMessageBox::Ok); msgBox.exec(); } @@ -1726,7 +1781,7 @@ void Application::checkFPS() { void Application::idle() { PerformanceTimer perfTimer("idle"); - + if (_aboutToQuit) { return; // bail early, nothing to do here. } @@ -1856,16 +1911,14 @@ void Application::setEnableVRMode(bool enableVRMode) { OculusManager::recalibrate(); } else { OculusManager::abandonCalibration(); - + _mirrorCamera.setHmdPosition(glm::vec3()); _mirrorCamera.setHmdRotation(glm::quat()); _myCamera.setHmdPosition(glm::vec3()); _myCamera.setHmdRotation(glm::quat()); } - + resizeGL(); - - updateCursorVisibility(); } #endif @@ -1884,7 +1937,7 @@ ivec2 Application::getMouseDragStarted() const { FaceTracker* Application::getActiveFaceTracker() { auto faceshift = DependencyManager::get(); auto dde = DependencyManager::get(); - + return (dde->isActive() ? static_cast(dde.data()) : (faceshift->isActive() ? static_cast(faceshift.data()) : NULL)); } @@ -2017,14 +2070,14 @@ void Application::saveSettings() { bool Application::importEntities(const QString& urlOrFilename) { _entityClipboard.eraseAllOctreeElements(); - + QUrl url(urlOrFilename); - + // if the URL appears to be invalid or relative, then it is probably a local file if (!url.isValid() || url.isRelative()) { url = QUrl::fromLocalFile(urlOrFilename); } - + bool success = _entityClipboard.readFromURL(url.toString()); if (success) { _entityClipboard.reaverageOctreeElements(); @@ -2048,7 +2101,7 @@ void Application::initDisplay() { void Application::init() { // Make sure Login state is up to date DependencyManager::get()->toggleLoginDialog(); - + _environment.init(); Q_ASSERT(_offscreenContext->getContext() == QOpenGLContext::currentContext()); @@ -2079,7 +2132,7 @@ void Application::init() { _timerStart.start(); _lastTimeUpdated.start(); - + // when --url in command line, teleport to location const QString HIFI_URL_COMMAND_LINE_KEY = "--url"; int urlIndex = arguments().indexOf(HIFI_URL_COMMAND_LINE_KEY); @@ -2087,11 +2140,11 @@ void Application::init() { if (urlIndex != -1) { addressLookupString = arguments().value(urlIndex + 1); } - + DependencyManager::get()->loadSettings(addressLookupString); - + qCDebug(interfaceapp) << "Loaded settings"; - + #ifdef __APPLE__ if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseEnabled)) { // on OS X we only setup sixense if the user wants it on - this allows running without the hid_init crash @@ -2199,7 +2252,7 @@ void Application::updateMouseRay() { PickRay pickRay = computePickRay(getTrueMouseX(), getTrueMouseY()); _mouseRayOrigin = pickRay.origin; _mouseRayDirection = pickRay.direction; - + // adjust for mirroring if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { glm::vec3 mouseRayOffset = _mouseRayOrigin - _viewFrustum.getPosition(); @@ -2233,28 +2286,27 @@ void Application::updateMyAvatarLookAtPosition() { } } #endif - + } else { - AvatarSharedPointer lookingAt = _myAvatar->getLookAtTargetAvatar().toStrongRef(); - if (lookingAt && _myAvatar != lookingAt.data()) { - + AvatarSharedPointer lookingAt = _myAvatar->getLookAtTargetAvatar().lock(); + if (lookingAt && _myAvatar != lookingAt.get()) { isLookingAtSomeone = true; // If I am looking at someone else, look directly at one of their eyes if (tracker && !tracker->isMuted()) { // If a face tracker is active, look at the eye for the side my gaze is biased toward if (tracker->getEstimatedEyeYaw() > _myAvatar->getHead()->getFinalYaw()) { // Look at their right eye - lookAtSpot = static_cast(lookingAt.data())->getHead()->getRightEyePosition(); + lookAtSpot = static_cast(lookingAt.get())->getHead()->getRightEyePosition(); } else { // Look at their left eye - lookAtSpot = static_cast(lookingAt.data())->getHead()->getLeftEyePosition(); + lookAtSpot = static_cast(lookingAt.get())->getHead()->getLeftEyePosition(); } } else { // Need to add randomly looking back and forth between left and right eye for case with no tracker if (_myAvatar->isLookingAtLeftEye()) { - lookAtSpot = static_cast(lookingAt.data())->getHead()->getLeftEyePosition(); + lookAtSpot = static_cast(lookingAt.get())->getHead()->getLeftEyePosition(); } else { - lookAtSpot = static_cast(lookingAt.data())->getHead()->getRightEyePosition(); + lookAtSpot = static_cast(lookingAt.get())->getHead()->getRightEyePosition(); } } } else { @@ -2324,7 +2376,7 @@ void Application::updateDialogs(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateDialogs()"); auto dialogsManager = DependencyManager::get(); - + // Update bandwidth dialog, if any BandwidthDialog* bandwidthDialog = dialogsManager->getBandwidthDialog(); if (bandwidthDialog) { @@ -2347,17 +2399,8 @@ void Application::updateCursor(float deltaTime) { lastMousePos = QCursor::pos(); } -void Application::updateCursorVisibility() { - if (!_cursorVisible || getActiveDisplayPlugin()->isStereo()) { - _window->setCursor(Qt::BlankCursor); - } else { - _window->unsetCursor(); - } -} - void Application::setCursorVisible(bool visible) { _cursorVisible = visible; - updateCursorVisibility(); } void Application::update(float deltaTime) { @@ -2528,7 +2571,7 @@ void Application::update(float deltaTime) { } } - // send packet containing downstream audio stats to the AudioMixer + // send packet containing downstream audio stats to the AudioMixer { quint64 sinceLastNack = now - _lastSendDownstreamAudioStats; if (sinceLastNack > TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS) { @@ -2550,64 +2593,64 @@ int Application::sendNackPackets() { // iterates thru all nodes in NodeList auto nodeList = DependencyManager::get(); - + nodeList->eachNode([&](const SharedNodePointer& node){ - + if (node->getActiveSocket() && node->getType() == NodeType::EntityServer) { - + QUuid nodeUUID = node->getUUID(); - + // if there are octree packets from this node that are waiting to be processed, // don't send a NACK since the missing packets may be among those waiting packets. if (_octreeProcessor.hasPacketsToProcessFrom(nodeUUID)) { return; } - + _octreeSceneStatsLock.lockForRead(); - + // retreive octree scene stats of this node if (_octreeServerSceneStats.find(nodeUUID) == _octreeServerSceneStats.end()) { _octreeSceneStatsLock.unlock(); return; } - + // get sequence number stats of node, prune its missing set, and make a copy of the missing set SequenceNumberStats& sequenceNumberStats = _octreeServerSceneStats[nodeUUID].getIncomingOctreeSequenceNumberStats(); sequenceNumberStats.pruneMissingSet(); const QSet missingSequenceNumbers = sequenceNumberStats.getMissingSet(); - + _octreeSceneStatsLock.unlock(); - + // construct nack packet(s) for this node int numSequenceNumbersAvailable = missingSequenceNumbers.size(); QSet::const_iterator missingSequenceNumbersIterator = missingSequenceNumbers.constBegin(); while (numSequenceNumbersAvailable > 0) { - + char* dataAt = packet; int bytesRemaining = MAX_PACKET_SIZE; - + // pack header int numBytesPacketHeader = nodeList->populatePacketHeader(packet, PacketTypeOctreeDataNack); dataAt += numBytesPacketHeader; bytesRemaining -= numBytesPacketHeader; - + // calculate and pack the number of sequence numbers int numSequenceNumbersRoomFor = (bytesRemaining - sizeof(uint16_t)) / sizeof(OCTREE_PACKET_SEQUENCE); uint16_t numSequenceNumbers = min(numSequenceNumbersAvailable, numSequenceNumbersRoomFor); uint16_t* numSequenceNumbersAt = (uint16_t*)dataAt; *numSequenceNumbersAt = numSequenceNumbers; dataAt += sizeof(uint16_t); - + // pack sequence numbers for (int i = 0; i < numSequenceNumbers; i++) { OCTREE_PACKET_SEQUENCE* sequenceNumberAt = (OCTREE_PACKET_SEQUENCE*)dataAt; *sequenceNumberAt = *missingSequenceNumbersIterator; dataAt += sizeof(OCTREE_PACKET_SEQUENCE); - + missingSequenceNumbersIterator++; } numSequenceNumbersAvailable -= numSequenceNumbers; - + // send it nodeList->writeUnverifiedDatagram(packet, dataAt - packet, node); packetsSent++; @@ -2649,7 +2692,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node int unknownJurisdictionServers = 0; auto nodeList = DependencyManager::get(); - + nodeList->eachNode([&](const SharedNodePointer& node) { // only send to the NodeTypes that are serverType if (node->getActiveSocket() && node->getType() == serverType) { @@ -2709,17 +2752,17 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node if (wantExtraDebugging) { qCDebug(interfaceapp, "perServerPPS: %d perUnknownServer: %d", perServerPPS, perUnknownServer); } - + nodeList->eachNode([&](const SharedNodePointer& node){ // only send to the NodeTypes that are serverType if (node->getActiveSocket() && node->getType() == serverType) { - + // get the server bounds for this server QUuid nodeUUID = node->getUUID(); - + bool inView = false; bool unknownView = false; - + // if we haven't heard from this voxel server, go ahead and send it a query, so we // can get the jurisdiction... if (jurisdictions.find(nodeUUID) == jurisdictions.end()) { @@ -2729,9 +2772,9 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node } } else { const JurisdictionMap& map = (jurisdictions)[nodeUUID]; - + unsigned char* rootCode = map.getRootOctalCode(); - + if (rootCode) { VoxelPositionSize rootDetails; voxelDetailsForCode(rootCode, rootDetails); @@ -2741,7 +2784,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node rootDetails.s * TREE_SCALE); - + ViewFrustum::location serverFrustumLocation = _viewFrustum.cubeInFrustum(serverBounds); if (serverFrustumLocation != ViewFrustum::OUTSIDE) { inView = true; @@ -2754,7 +2797,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node } } } - + if (inView) { _octreeQuery.setMaxQueryPacketsPerSecond(perServerPPS); } else if (unknownView) { @@ -2762,7 +2805,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node qCDebug(interfaceapp) << "no known jurisdiction for node " << *node << ", give it budget of " << perUnknownServer << " to send us jurisdiction."; } - + // set the query's position/orientation to be degenerate in a manner that will get the scene quickly // If there's only one server, then don't do this, and just let the normal voxel query pass through // as expected... this way, we will actually get a valid scene if there is one to be seen @@ -2789,12 +2832,12 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node // insert packet type/version and node UUID endOfQueryPacket += nodeList->populatePacketHeader(reinterpret_cast(endOfQueryPacket), packetType); - + // encode the query data... endOfQueryPacket += _octreeQuery.getBroadcastData(endOfQueryPacket); - + int packetLength = endOfQueryPacket - queryPacket; - + // make sure we still have an active socket nodeList->writeUnverifiedDatagram(reinterpret_cast(queryPacket), packetLength, node); } @@ -2808,6 +2851,7 @@ bool Application::isHMDMode() const { QRect Application::getDesirableApplicationGeometry() { QRect applicationGeometry = getWindow()->geometry(); #if 0 + // If our parent window is on the HMD, then don't use its geometry, instead use // the "main screen" geometry. HMDToolsDialog* hmdTools = DependencyManager::get()->getHMDToolsDialog(); @@ -2838,7 +2882,7 @@ glm::vec3 Application::getSunDirection() { // FIXME, preprocessor guard this check to occur only in DEBUG builds static QThread * activeRenderingThread = nullptr; -void Application::updateShadowMap() { +void Application::updateShadowMap(RenderArgs* renderArgs) { activeRenderingThread = QThread::currentThread(); PerformanceTimer perfTimer("shadowMap"); @@ -2851,14 +2895,14 @@ void Application::updateShadowMap() { glm::vec3 lightDirection = getSunDirection(); glm::quat rotation = rotationBetween(IDENTITY_FRONT, lightDirection); glm::quat inverseRotation = glm::inverse(rotation); - + const float SHADOW_MATRIX_DISTANCES[] = { 0.0f, 2.0f, 6.0f, 14.0f, 30.0f }; const glm::vec2 MAP_COORDS[] = { glm::vec2(0.0f, 0.0f), glm::vec2(0.5f, 0.0f), glm::vec2(0.0f, 0.5f), glm::vec2(0.5f, 0.5f) }; - + float frustumScale = 1.0f / (_viewFrustum.getFarClip() - _viewFrustum.getNearClip()); _myCamera.loadViewFrustum(_viewFrustum); - + int matrixCount = 1; //int targetSize = fbo->width(); int sourceSize = shadowFramebuffer->getWidth(); @@ -2900,12 +2944,12 @@ void Application::updateShadowMap() { _shadowDistances[i] = -glm::distance(_viewFrustum.getPosition(), center) - radius * RADIUS_SCALE; } center = inverseRotation * center; - + // to reduce texture "shimmer," move in texel increments float texelSize = (2.0f * radius) / targetSize; center = glm::vec3(roundf(center.x / texelSize) * texelSize, roundf(center.y / texelSize) * texelSize, roundf(center.z / texelSize) * texelSize); - + glm::vec3 minima(center.x - radius, center.y - radius, center.z - radius); glm::vec3 maxima(center.x + radius, center.y + radius, center.z + radius); @@ -2947,25 +2991,9 @@ void Application::updateShadowMap() { glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(1.1f, 4.0f); // magic numbers courtesy http://www.eecs.berkeley.edu/~ravir/6160/papers/shadowmaps.ppt - { - PerformanceTimer perfTimer("avatarManager"); - DependencyManager::get()->renderAvatars(RenderArgs::SHADOW_RENDER_MODE); - } - { PerformanceTimer perfTimer("entities"); - _entities.render(RenderArgs::SHADOW_RENDER_MODE); - } - - // render JS/scriptable overlays - { - PerformanceTimer perfTimer("3dOverlays"); - _overlays.renderWorld(false, RenderArgs::SHADOW_RENDER_MODE); - } - - { - PerformanceTimer perfTimer("3dOverlaysFront"); - _overlays.renderWorld(true, RenderArgs::SHADOW_RENDER_MODE); + _entities.render(renderArgs); } glDisable(GL_POLYGON_OFFSET_FILL); @@ -2977,7 +3005,8 @@ void Application::updateShadowMap() { glMatrixMode(GL_MODELVIEW); } - + + glViewport(0, 0, _glWidget->getDeviceWidth(), _glWidget->getDeviceHeight()); activeRenderingThread = nullptr; } @@ -3008,11 +3037,11 @@ bool Application::shouldRenderMesh(float largestDimension, float distanceToCamer return DependencyManager::get()->shouldRenderMesh(largestDimension, distanceToCamera); } -float Application::getSizeScale() const { +float Application::getSizeScale() const { return DependencyManager::get()->getOctreeSizeScale(); } -int Application::getBoundaryLevelAdjust() const { +int Application::getBoundaryLevelAdjust() const { return DependencyManager::get()->getBoundaryLevelAdjust(); } @@ -3033,22 +3062,22 @@ PickRay Application::computePickRay(float x, float y) const { return result; } -QImage Application::renderAvatarBillboard() { +QImage Application::renderAvatarBillboard(RenderArgs* renderArgs) { auto primaryFramebuffer = DependencyManager::get()->getPrimaryFramebuffer(); glBindFramebuffer(GL_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(primaryFramebuffer)); - + // clear the alpha channel so the background is transparent glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); - + // the "glow" here causes an alpha of one - Glower glower; - + Glower glower(renderArgs); + const int BILLBOARD_SIZE = 64; #if 0 - renderRearViewMirror(QRect(0, _glWidget->getDeviceHeight() - BILLBOARD_SIZE, + renderRearViewMirror(renderArgs, QRect(0, _glWidget->getDeviceHeight() - BILLBOARD_SIZE, BILLBOARD_SIZE, BILLBOARD_SIZE), true); #endif @@ -3056,9 +3085,9 @@ QImage Application::renderAvatarBillboard() { QImage image(BILLBOARD_SIZE, BILLBOARD_SIZE, QImage::Format_ARGB32); glReadPixels(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE, GL_BGRA, GL_UNSIGNED_BYTE, image.bits()); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - + glBindFramebuffer(GL_FRAMEBUFFER, 0); - + return image; } @@ -3102,7 +3131,143 @@ const ViewFrustum* Application::getDisplayViewFrustum() const { return &_displayViewFrustum; } -void Application::displaySide(const Camera& theCamera, bool selfAvatarOnly) { +// WorldBox Render Data & rendering functions + +class WorldBoxRenderData { +public: + typedef render::Payload Payload; + typedef Payload::DataPointer Pointer; + + int _val = 0; + static render::ItemID _item; // unique WorldBoxRenderData +}; + +render::ItemID WorldBoxRenderData::_item = 0; + +namespace render { + template <> const ItemKey payloadGetKey(const WorldBoxRenderData::Pointer& stuff) { return ItemKey::Builder::opaqueShape(); } + template <> const Item::Bound payloadGetBound(const WorldBoxRenderData::Pointer& stuff) { return Item::Bound(); } + template <> void payloadRender(const WorldBoxRenderData::Pointer& stuff, RenderArgs* args) { + if (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE && Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { + PerformanceTimer perfTimer("worldBox"); + + auto& batch = *args->_batch; + DependencyManager::get()->bindSimpleProgram(batch); + renderWorldBox(batch); + } + } +} + +// Background Render Data & rendering functions +class BackgroundRenderData { +public: + typedef render::Payload Payload; + typedef Payload::DataPointer Pointer; + + Stars _stars; + Environment* _environment; + + BackgroundRenderData(Environment* environment) : _environment(environment) { + } + + static render::ItemID _item; // unique WorldBoxRenderData +}; + +render::ItemID BackgroundRenderData::_item = 0; + +namespace render { + template <> const ItemKey payloadGetKey(const BackgroundRenderData::Pointer& stuff) { return ItemKey::Builder::background(); } + template <> const Item::Bound payloadGetBound(const BackgroundRenderData::Pointer& stuff) { return Item::Bound(); } + template <> void payloadRender(const BackgroundRenderData::Pointer& background, RenderArgs* args) { + + // Background rendering decision + auto skyStage = DependencyManager::get()->getSkyStage(); + auto skybox = model::SkyboxPointer(); + if (skyStage->getBackgroundMode() == model::SunSkyStage::NO_BACKGROUND) { + } else if (skyStage->getBackgroundMode() == model::SunSkyStage::SKY_DOME) { + if (/*!selfAvatarOnly &&*/ Menu::getInstance()->isOptionChecked(MenuOption::Stars)) { + PerformanceTimer perfTimer("stars"); + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + "Application::payloadRender() ... stars..."); + if (!background->_stars.isStarsLoaded()) { + background->_stars.generate(STARFIELD_NUM_STARS, STARFIELD_SEED); + } + // should be the first rendering pass - w/o depth buffer / lighting + + // compute starfield alpha based on distance from atmosphere + float alpha = 1.0f; + bool hasStars = true; + + if (Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) { + // TODO: handle this correctly for zones + const EnvironmentData& closestData = background->_environment->getClosestData(args->_viewFrustum->getPosition()); // was theCamera instead of _viewFrustum + + if (closestData.getHasStars()) { + const float APPROXIMATE_DISTANCE_FROM_HORIZON = 0.1f; + const float DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON = 0.2f; + + glm::vec3 sunDirection = (args->_viewFrustum->getPosition()/*getAvatarPosition()*/ - closestData.getSunLocation()) + / closestData.getAtmosphereOuterRadius(); + float height = glm::distance(args->_viewFrustum->getPosition()/*theCamera.getPosition()*/, closestData.getAtmosphereCenter()); + if (height < closestData.getAtmosphereInnerRadius()) { + // If we're inside the atmosphere, then determine if our keyLight is below the horizon + alpha = 0.0f; + + if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) { + float directionY = glm::clamp(sunDirection.y, + -APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON) + + APPROXIMATE_DISTANCE_FROM_HORIZON; + alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON); + } + + + } else if (height < closestData.getAtmosphereOuterRadius()) { + alpha = (height - closestData.getAtmosphereInnerRadius()) / + (closestData.getAtmosphereOuterRadius() - closestData.getAtmosphereInnerRadius()); + + if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) { + float directionY = glm::clamp(sunDirection.y, + -APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON) + + APPROXIMATE_DISTANCE_FROM_HORIZON; + alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON); + } + } + } else { + hasStars = false; + } + } + + // finally render the starfield + if (hasStars) { + background->_stars.render(args->_viewFrustum->getFieldOfView(), args->_viewFrustum->getAspectRatio(), args->_viewFrustum->getNearClip(), alpha); + } + + // draw the sky dome + if (/*!selfAvatarOnly &&*/ Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) { + PerformanceTimer perfTimer("atmosphere"); + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + "Application::displaySide() ... atmosphere..."); + background->_environment->renderAtmospheres(*(args->_viewFrustum)); + } + + } + } else if (skyStage->getBackgroundMode() == model::SunSkyStage::SKY_BOX) { + PerformanceTimer perfTimer("skybox"); + + skybox = skyStage->getSkybox(); + if (skybox) { + gpu::Batch batch; + model::Skybox::render(batch, *(Application::getInstance()->getDisplayViewFrustum()), *skybox); + + gpu::GLBackend::renderBatch(batch, true); + glUseProgram(0); + } + } + } +} + + +void Application::displaySide(RenderArgs* renderArgs, const Camera& theCamera, bool selfAvatarOnly) { activeRenderingThread = QThread::currentThread(); PROFILE_RANGE(__FUNCTION__); PerformanceTimer perfTimer("display"); @@ -3138,6 +3303,7 @@ void Application::displaySide(const Camera& theCamera, bool selfAvatarOnly) { if (theCamera.getMode() == CAMERA_MODE_MIRROR) { viewTransform.setScale(Transform::Vec3(-1.0f, 1.0f, 1.0f)); } + setViewTransform(viewTransform); // Setup 3D lights (after the camera transform, so that they are positioned in world space) @@ -3160,86 +3326,17 @@ void Application::displaySide(const Camera& theCamera, bool selfAvatarOnly) { glTexGenfv(GL_R, GL_EYE_PLANE, (const GLfloat*)&_shadowMatrices[i][2]); } + // The pending changes collecting the changes here + render::PendingChanges pendingChanges; + // Background rendering decision - auto skyStage = DependencyManager::get()->getSkyStage(); - auto skybox = model::SkyboxPointer(); - if (skyStage->getBackgroundMode() == model::SunSkyStage::NO_BACKGROUND) { - } else if (skyStage->getBackgroundMode() == model::SunSkyStage::SKY_DOME) { - if (!selfAvatarOnly && Menu::getInstance()->isOptionChecked(MenuOption::Stars)) { - PerformanceTimer perfTimer("stars"); - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "Application::displaySide() ... stars..."); - if (!_stars.isStarsLoaded()) { - _stars.generate(STARFIELD_NUM_STARS, STARFIELD_SEED); - } - // should be the first rendering pass - w/o depth buffer / lighting + if (BackgroundRenderData::_item == 0) { + auto backgroundRenderData = BackgroundRenderData::Pointer(new BackgroundRenderData(&_environment)); + auto backgroundRenderPayload = render::PayloadPointer(new BackgroundRenderData::Payload(backgroundRenderData)); + BackgroundRenderData::_item = _main3DScene->allocateID(); + pendingChanges.resetItem(WorldBoxRenderData::_item, backgroundRenderPayload); + } else { - // compute starfield alpha based on distance from atmosphere - float alpha = 1.0f; - bool hasStars = true; - - if (Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) { - // TODO: handle this correctly for zones - const EnvironmentData& closestData = _environment.getClosestData(_displayViewFrustum.getPosition()); - - if (closestData.getHasStars()) { - const float APPROXIMATE_DISTANCE_FROM_HORIZON = 0.1f; - const float DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON = 0.2f; - - glm::vec3 sunDirection = (getAvatarPosition() - closestData.getSunLocation()) - / closestData.getAtmosphereOuterRadius(); - float height = glm::distance(_displayViewFrustum.getPosition(), closestData.getAtmosphereCenter()); - if (height < closestData.getAtmosphereInnerRadius()) { - // If we're inside the atmosphere, then determine if our keyLight is below the horizon - alpha = 0.0f; - - if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) { - float directionY = glm::clamp(sunDirection.y, - -APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON) - + APPROXIMATE_DISTANCE_FROM_HORIZON; - alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON); - } - - - } else if (height < closestData.getAtmosphereOuterRadius()) { - alpha = (height - closestData.getAtmosphereInnerRadius()) / - (closestData.getAtmosphereOuterRadius() - closestData.getAtmosphereInnerRadius()); - - if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) { - float directionY = glm::clamp(sunDirection.y, - -APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON) - + APPROXIMATE_DISTANCE_FROM_HORIZON; - alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON); - } - } - } else { - hasStars = false; - } - } - - // finally render the starfield - if (hasStars) { - _stars.render(_displayViewFrustum.getFieldOfView(), _displayViewFrustum.getAspectRatio(), _displayViewFrustum.getNearClip(), alpha); - } - - // draw the sky dome - if (!selfAvatarOnly && Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) { - PerformanceTimer perfTimer("atmosphere"); - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "Application::displaySide() ... atmosphere..."); - _environment.renderAtmospheres(_displayViewFrustum.getPosition()); - } - - } - } else if (skyStage->getBackgroundMode() == model::SunSkyStage::SKY_BOX) { - skybox = skyStage->getSkybox(); - if (skybox) { - gpu::Batch batch; - model::Skybox::render(batch, _displayViewFrustum, *skybox); - - gpu::GLBackend::renderBatch(batch); - glUseProgram(0); - } } if (Menu::getInstance()->isOptionChecked(MenuOption::Wireframe)) { @@ -3249,13 +3346,10 @@ void Application::displaySide(const Camera& theCamera, bool selfAvatarOnly) { glEnable(GL_LIGHTING); glEnable(GL_DEPTH_TEST); - DependencyManager::get()->prepare(); + // Assuming nothing get's rendered through that if (!selfAvatarOnly) { - // draw a red sphere - float originSphereRadius = 0.05f; - DependencyManager::get()->renderSphere(originSphereRadius, 15, 15, glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)); - + // render models... if (DependencyManager::get()->shouldRenderEntities()) { PerformanceTimer perfTimer("entities"); @@ -3275,18 +3369,9 @@ void Application::displaySide(const Camera& theCamera, bool selfAvatarOnly) { if (theCamera.getMode() == CAMERA_MODE_MIRROR) { renderMode = RenderArgs::MIRROR_RENDER_MODE; } - _entities.render(renderMode, RenderArgs::MONO, renderDebugFlags); - - if (!Menu::getInstance()->isOptionChecked(MenuOption::Wireframe)) { - // Restaure polygon mode - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - } - } - - // render JS/scriptable overlays - { - PerformanceTimer perfTimer("3dOverlays"); - _overlays.renderWorld(false); + renderArgs->_renderMode = renderMode; + renderArgs->_debugFlags = renderDebugFlags; + _entities.render(renderArgs); } // render the ambient occlusion effect if enabled @@ -3297,13 +3382,21 @@ void Application::displaySide(const Camera& theCamera, bool selfAvatarOnly) { DependencyManager::get()->render(); } } - - bool mirrorMode = (theCamera.getMode() == CAMERA_MODE_MIRROR); - { - PerformanceTimer perfTimer("avatars"); - DependencyManager::get()->renderAvatars(mirrorMode ? RenderArgs::MIRROR_RENDER_MODE : RenderArgs::NORMAL_RENDER_MODE, - false, selfAvatarOnly); + // Make sure the WorldBox is in the scene + if (WorldBoxRenderData::_item == 0) { + auto worldBoxRenderData = WorldBoxRenderData::Pointer(new WorldBoxRenderData()); + auto worldBoxRenderPayload = render::PayloadPointer(new WorldBoxRenderData::Payload(worldBoxRenderData)); + + WorldBoxRenderData::_item = _main3DScene->allocateID(); + + pendingChanges.resetItem(WorldBoxRenderData::_item, worldBoxRenderPayload); + } else { + + pendingChanges.updateItem(WorldBoxRenderData::_item, + [](WorldBoxRenderData& payload) { + payload._val++; + }); } if (true) { @@ -3312,20 +3405,55 @@ void Application::displaySide(const Camera& theCamera, bool selfAvatarOnly) { auto skyStage = DependencyManager::get()->getSkyStage(); DependencyManager::get()->setGlobalLight(skyStage->getSunLight()->getDirection(), skyStage->getSunLight()->getColor(), skyStage->getSunLight()->getIntensity(), skyStage->getSunLight()->getAmbientIntensity()); DependencyManager::get()->setGlobalAtmosphere(skyStage->getAtmosphere()); - DependencyManager::get()->setGlobalSkybox(skybox); - PROFILE_RANGE("DeferredLighting"); - PerformanceTimer perfTimer("lighting"); - Q_ASSERT(_offscreenContext->getContext() == QOpenGLContext::currentContext()); - DependencyManager::get()->render(); + auto skybox = model::SkyboxPointer(); + if (skyStage->getBackgroundMode() == model::SunSkyStage::SKY_BOX) { + skybox = skyStage->getSkybox(); + } + DependencyManager::get()->setGlobalSkybox(skybox); } { - PerformanceTimer perfTimer("avatarsPostLighting"); - DependencyManager::get()->renderAvatars(mirrorMode ? RenderArgs::MIRROR_RENDER_MODE : RenderArgs::NORMAL_RENDER_MODE, - true, selfAvatarOnly); + PerformanceTimer perfTimer("SceneProcessPendingChanges"); + _main3DScene->enqueuePendingChanges(pendingChanges); + + _main3DScene->processPendingChangesQueue(); + } + + // For now every frame pass the renderContext + { + PerformanceTimer perfTimer("EngineRun"); + render::RenderContext renderContext; + + auto sceneInterface = DependencyManager::get(); + + renderContext._cullOpaque = sceneInterface->doEngineCullOpaque(); + renderContext._sortOpaque = sceneInterface->doEngineSortOpaque(); + renderContext._renderOpaque = sceneInterface->doEngineRenderOpaque(); + renderContext._cullTransparent = sceneInterface->doEngineCullTransparent(); + renderContext._sortTransparent = sceneInterface->doEngineSortTransparent(); + renderContext._renderTransparent = sceneInterface->doEngineRenderTransparent(); + + renderContext._maxDrawnOpaqueItems = sceneInterface->getEngineMaxDrawnOpaqueItems(); + renderContext._maxDrawnTransparentItems = sceneInterface->getEngineMaxDrawnTransparentItems(); + + renderArgs->_shouldRender = LODManager::shouldRender; + + renderContext.args = renderArgs; + renderArgs->_viewFrustum = getDisplayViewFrustum(); + _renderEngine->setRenderContext(renderContext); + + // Before the deferred pass, let's try to use the render engine + _renderEngine->run(); + + auto engineRC = _renderEngine->getRenderContext(); + sceneInterface->setEngineFeedOpaqueItems(engineRC->_numFeedOpaqueItems); + sceneInterface->setEngineDrawnOpaqueItems(engineRC->_numDrawnOpaqueItems); + + sceneInterface->setEngineFeedTransparentItems(engineRC->_numFeedTransparentItems); + sceneInterface->setEngineDrawnTransparentItems(engineRC->_numDrawnTransparentItems); + } - //Render the sixense lasers if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) { _myAvatar->renderLaserPointers(); @@ -3333,12 +3461,6 @@ void Application::displaySide(const Camera& theCamera, bool selfAvatarOnly) { if (!selfAvatarOnly) { _nodeBoundsDisplay.draw(); - - // Render the world box - if (!mirrorMode && Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { - PerformanceTimer perfTimer("worldBox"); - renderWorldBox(); - } // render octree fades if they exist if (_octreeFades.size() > 0) { @@ -3347,7 +3469,7 @@ void Application::displaySide(const Camera& theCamera, bool selfAvatarOnly) { "Application::displaySide() ... octree fades..."); _octreeFadesLock.lockForWrite(); for(std::vector::iterator fade = _octreeFades.begin(); fade != _octreeFades.end();) { - fade->render(); + fade->render(renderArgs); if(fade->isDone()) { fade = _octreeFades.erase(fade); } else { @@ -3368,14 +3490,6 @@ void Application::displaySide(const Camera& theCamera, bool selfAvatarOnly) { glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } - // Render 3D overlays that should be drawn in front - { - PerformanceTimer perfTimer("3dOverlaysFront"); - glClear(GL_DEPTH_BUFFER_BIT); - Glower glower; // Sets alpha to 1.0 - _overlays.renderWorld(true); - } - activeRenderingThread = nullptr; } @@ -3413,11 +3527,37 @@ bool Application::getShadowsEnabled() { menubar->isOptionChecked(MenuOption::CascadedShadows); } -bool Application::getCascadeShadowsEnabled() { - return Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows); +bool Application::getCascadeShadowsEnabled() { + return Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows); } -void Application::renderRearViewMirror(const QRect& region, bool billboard) { +glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) { + float horizontalScale = _glWidget->getDeviceWidth() / 2.0f; + float verticalScale = _glWidget->getDeviceHeight() / 2.0f; + + // -1,-1 is 0,windowHeight + // 1,1 is windowWidth,0 + + // -1,1 1,1 + // +-----------------------+ + // | | | + // | | | + // | -1,0 | | + // |-----------+-----------| + // | 0,0 | + // | | | + // | | | + // | | | + // +-----------------------+ + // -1,-1 1,-1 + + glm::vec2 screenPoint((projectedPoint.x + 1.0) * horizontalScale, + ((projectedPoint.y + 1.0) * -verticalScale) + _glWidget->getDeviceHeight()); + + return screenPoint; +} + +void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& region, bool billboard) { // Grab current viewport to reset it at the end int viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); @@ -3435,22 +3575,22 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale()); } else { // HEAD zoom level - // FIXME note that the positioing of the camera relative to the avatar can suffer limited - // precision as the user's position moves further away from the origin. Thus at - // /1e7,1e7,1e7 (well outside the buildable volume) the mirror camera veers and sways - // wildly as you rotate your avatar because the floating point values are becoming - // larger, squeezing out the available digits of precision you have available at the - // human scale for camera positioning. + // FIXME note that the positioing of the camera relative to the avatar can suffer limited + // precision as the user's position moves further away from the origin. Thus at + // /1e7,1e7,1e7 (well outside the buildable volume) the mirror camera veers and sways + // wildly as you rotate your avatar because the floating point values are becoming + // larger, squeezing out the available digits of precision you have available at the + // human scale for camera positioning. - // Previously there was a hack to correct this using the mechanism of repositioning - // the avatar at the origin of the world for the purposes of rendering the mirror, - // but it resulted in failing to render the avatar's head model in the mirror view - // when in first person mode. Presumably this was because of some missed culling logic - // that was not accounted for in the hack. + // Previously there was a hack to correct this using the mechanism of repositioning + // the avatar at the origin of the world for the purposes of rendering the mirror, + // but it resulted in failing to render the avatar's head model in the mirror view + // when in first person mode. Presumably this was because of some missed culling logic + // that was not accounted for in the hack. - // This was removed in commit 71e59cfa88c6563749594e25494102fe01db38e9 but could be further + // This was removed in commit 71e59cfa88c6563749594e25494102fe01db38e9 but could be further // investigated in order to adapt the technique while fixing the head rendering issue, - // but the complexity of the hack suggests that a better approach + // but the complexity of the hack suggests that a better approach _mirrorCamera.setPosition(_myAvatar->getHead()->getEyePosition() + _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale()); } @@ -3461,7 +3601,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { if (billboard) { QSize size = DependencyManager::get()->getFrameBufferSize(); glViewport(region.x(), size.height() - region.y() - region.height(), region.width(), region.height()); - glScissor(region.x(), size.height() - region.y() - region.height(), region.width(), region.height()); + glScissor(region.x(), size.height() - region.y() - region.height(), region.width(), region.height()); } else { // if not rendering the billboard, the region is in device independent coordinates; must convert to device QSize size = DependencyManager::get()->getFrameBufferSize(); @@ -3475,12 +3615,11 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { // render rear mirror view glPushMatrix(); - displaySide(_mirrorCamera, true); + displaySide(renderArgs, _mirrorCamera, true, billboard); glPopMatrix(); if (!billboard) { - glm::vec2 mpos = getActiveDisplayPlugin()->getUiMousePosition(); - _rearMirrorTools->render(false, QPoint(mpos.x, mpos.y)); + _rearMirrorTools->render(renderArgs, false, _glWidget->mapFromGlobal(QCursor::pos())); } // reset Viewport and projection matrix glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); @@ -3500,6 +3639,7 @@ void Application::resetSensors() { QPoint windowCenter = mainWindow->geometry().center(); _glWidget->cursor().setPos(currentScreen, windowCenter); #endif + _myAvatar->reset(); QMetaObject::invokeMethod(DependencyManager::get().data(), "reset", Qt::QueuedConnection); @@ -3531,12 +3671,12 @@ void Application::updateWindowTitle(){ QString connectionStatus = nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED) "; QString username = AccountManager::getInstance().getAccountInfo().getUsername(); - QString currentPlaceName = DependencyManager::get()->getRootPlaceName(); - + QString currentPlaceName = DependencyManager::get()->getHost(); + if (currentPlaceName.isEmpty()) { currentPlaceName = nodeList->getDomainHandler().getHostname(); } - + QString title = QString() + (!username.isEmpty() ? username + " @ " : QString()) + currentPlaceName + connectionStatus + buildVersion; @@ -3550,7 +3690,6 @@ void Application::updateWindowTitle(){ void Application::clearDomainOctreeDetails() { qCDebug(interfaceapp) << "Clearing domain octree details..."; // reset the environment so that we don't erroneously end up with multiple - _environment.resetToDefault(); // reset our node to stats and node to jurisdiction maps... since these must be changing... _entityServerJurisdictions.lockForWrite(); @@ -3582,7 +3721,7 @@ void Application::domainConnectionDenied(const QString& reason) { void Application::connectedToDomain(const QString& hostname) { AccountManager& accountManager = AccountManager::getInstance(); const QUuid& domainID = DependencyManager::get()->getDomainHandler().getUUID(); - + if (accountManager.isLoggedIn() && !domainID.isNull()) { _notifiedPacketVersionMismatchThisDomain = false; } @@ -3766,7 +3905,7 @@ void Application::saveScripts() { Settings settings; settings.beginWriteArray(SETTINGS_KEY); settings.remove(""); - + QStringList runningScripts = getRunningScripts(); int i = 0; for (auto it = runningScripts.begin(); it != runningScripts.end(); ++it) { @@ -3817,7 +3956,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("Overlays", &_overlays); qScriptRegisterMetaType(scriptEngine, OverlayPropertyResultToScriptValue, OverlayPropertyResultFromScriptValue); - qScriptRegisterMetaType(scriptEngine, RayToOverlayIntersectionResultToScriptValue, + qScriptRegisterMetaType(scriptEngine, RayToOverlayIntersectionResultToScriptValue, RayToOverlayIntersectionResultFromScriptValue); QScriptValue windowValue = scriptEngine->registerGlobalObject("Window", DependencyManager::get().data()); @@ -3828,7 +3967,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri LocationScriptingInterface::locationSetter); scriptEngine->registerFunction("WebWindow", WebWindowClass::constructor, 1); - + scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance()); @@ -3840,7 +3979,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri qScriptRegisterMetaType(scriptEngine, DownloadInfoResultToScriptValue, DownloadInfoResultFromScriptValue); scriptEngine->registerGlobalObject("AvatarManager", DependencyManager::get().data()); - + scriptEngine->registerGlobalObject("Joysticks", &JoystickScriptingInterface::getInstance()); qScriptRegisterMetaType(scriptEngine, joystickToScriptValue, joystickFromScriptValue); @@ -3848,6 +3987,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("LODManager", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("Paths", DependencyManager::get().data()); + QScriptValue hmdInterface = scriptEngine->registerGlobalObject("HMD", &HMDScriptingInterface::getInstance()); scriptEngine->registerFunction(hmdInterface, "getHUDLookAtPosition2D", HMDScriptingInterface::getHUDLookAtPosition2D, 0); scriptEngine->registerFunction(hmdInterface, "getHUDLookAtPosition3D", HMDScriptingInterface::getHUDLookAtPosition3D, 0); @@ -3895,7 +4036,7 @@ void Application::initializeAcceptedFiles() { bool Application::canAcceptURL(const QString& urlString) { initializeAcceptedFiles(); - + QUrl url(urlString); if (urlString.startsWith(HIFI_URL_SCHEME)) { return true; @@ -3913,7 +4054,7 @@ bool Application::canAcceptURL(const QString& urlString) { bool Application::acceptURL(const QString& urlString) { initializeAcceptedFiles(); - + if (urlString.startsWith(HIFI_URL_SCHEME)) { // this is a hifi URL - have the AddressManager handle it QMetaObject::invokeMethod(DependencyManager::get().data(), "handleLookupString", @@ -3951,19 +4092,19 @@ bool Application::askToSetAvatarUrl(const QString& url) { msgBox.exec(); return false; } - + // Download the FST file, to attempt to determine its model type QVariantHash fstMapping = FSTReader::downloadMapping(url); - + FSTReader::ModelType modelType = FSTReader::predictModelType(fstMapping); - + QMessageBox msgBox; msgBox.setIcon(QMessageBox::Question); msgBox.setWindowTitle("Set Avatar"); QPushButton* headButton = NULL; QPushButton* bodyButton = NULL; QPushButton* bodyAndHeadButton = NULL; - + QString modelName = fstMapping["name"].toString(); QString message; QString typeInfo; @@ -3982,7 +4123,7 @@ bool Application::askToSetAvatarUrl(const QString& url) { message = QString("Would you like to use '") + modelName + QString("' for your avatar?"); bodyAndHeadButton = msgBox.addButton(tr("Yes"), QMessageBox::ActionRole); break; - + default: message = QString("Would you like to use '") + modelName + QString("' for some part of your avatar head?"); headButton = msgBox.addButton(tr("Use for Head"), QMessageBox::ActionRole); @@ -4008,7 +4149,7 @@ bool Application::askToSetAvatarUrl(const QString& url) { } else { qCDebug(interfaceapp) << "Declined to use the avatar: " << url; } - + return true; } @@ -4033,7 +4174,7 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser if (isAboutToQuit()) { return NULL; } - + QUrl scriptUrl(scriptFilename); const QString& scriptURLString = scriptUrl.toString(); if (_scriptEnginesHash.contains(scriptURLString) && loadScriptFromEditor @@ -4044,18 +4185,18 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser ScriptEngine* scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface); scriptEngine->setUserLoaded(isUserLoaded); - + if (scriptFilename.isNull()) { // this had better be the script editor (we should de-couple so somebody who thinks they are loading a script // doesn't just get an empty script engine) - + // we can complete setup now since there isn't a script we have to load registerScriptEngineWithApplicationServices(scriptEngine); } else { // connect to the appropriate signals of this script engine connect(scriptEngine, &ScriptEngine::scriptLoaded, this, &Application::handleScriptEngineLoaded); connect(scriptEngine, &ScriptEngine::errorLoadingScript, this, &Application::handleScriptLoadError); - + // get the script engine object to load the script at the designated script URL scriptEngine->loadURL(scriptUrl); } @@ -4070,11 +4211,11 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser void Application::handleScriptEngineLoaded(const QString& scriptFilename) { ScriptEngine* scriptEngine = qobject_cast(sender()); - + _scriptEnginesHash.insertMulti(scriptFilename, scriptEngine); _runningScriptsWidget->setRunningScripts(getRunningScripts()); UserActivityLogger::getInstance().loadedScript(scriptFilename); - + // register our application services and set it off on its own thread registerScriptEngineWithApplicationServices(scriptEngine); } @@ -4184,7 +4325,7 @@ void Application::updateMyAvatarTransform() { glm::vec3 newOriginOffset = avatarPosition; int halfExtent = (int)HALF_SIMULATION_EXTENT; for (int i = 0; i < 3; ++i) { - newOriginOffset[i] = (float)(glm::max(halfExtent, + newOriginOffset[i] = (float)(glm::max(halfExtent, ((int)(avatarPosition[i] / SIMULATION_OFFSET_QUANTIZATION)) * (int)SIMULATION_OFFSET_QUANTIZATION)); } // TODO: Andrew to replace this with method that actually moves existing object positions in PhysicsEngine @@ -4198,23 +4339,23 @@ void Application::domainSettingsReceived(const QJsonObject& domainSettingsObject const QString PER_VOXEL_COST_KEY = "per-voxel-credits"; const QString PER_METER_CUBED_COST_KEY = "per-meter-cubed-credits"; const QString VOXEL_WALLET_UUID = "voxel-wallet"; - + const QJsonObject& voxelObject = domainSettingsObject[VOXEL_SETTINGS_KEY].toObject(); - + qint64 satoshisPerVoxel = 0; qint64 satoshisPerMeterCubed = 0; QUuid voxelWalletUUID; - + if (!domainSettingsObject.isEmpty()) { float perVoxelCredits = (float) voxelObject[PER_VOXEL_COST_KEY].toDouble(); float perMeterCubedCredits = (float) voxelObject[PER_METER_CUBED_COST_KEY].toDouble(); - + satoshisPerVoxel = (qint64) floorf(perVoxelCredits * SATOSHIS_PER_CREDIT); satoshisPerMeterCubed = (qint64) floorf(perMeterCubedCredits * SATOSHIS_PER_CREDIT); - + voxelWalletUUID = QUuid(voxelObject[VOXEL_WALLET_UUID].toString()); } - + qCDebug(interfaceapp) << "Octree edits costs are" << satoshisPerVoxel << "per octree cell and" << satoshisPerMeterCubed << "per meter cubed"; qCDebug(interfaceapp) << "Destination wallet UUID for edit payments is" << voxelWalletUUID; } @@ -4420,7 +4561,7 @@ bool Application::isVSyncOn() const { } else { return true; } - */ + */ #endif return true; } @@ -4518,14 +4659,14 @@ void Application::notifyPacketVersionMismatch() { void Application::checkSkeleton() { if (_myAvatar->getSkeletonModel().isActive() && !_myAvatar->getSkeletonModel().hasSkeleton()) { qCDebug(interfaceapp) << "MyAvatar model has no skeleton"; - + QString message = "Your selected avatar body has no skeleton.\n\nThe default body will be loaded..."; QMessageBox msgBox; msgBox.setText(message); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); - + _myAvatar->useBodyURL(DEFAULT_BODY_MODEL_URL, "Default"); } else { _physicsEngine.setCharacterController(_myAvatar->getCharacterController()); @@ -4538,7 +4679,7 @@ void Application::showFriendsWindow() { const int FRIENDS_WINDOW_WIDTH = 290; const int FRIENDS_WINDOW_HEIGHT = 500; if (!_friendsWindow) { - _friendsWindow = new WebWindowClass(FRIENDS_WINDOW_TITLE, FRIENDS_WINDOW_URL, FRIENDS_WINDOW_WIDTH, + _friendsWindow = new WebWindowClass(FRIENDS_WINDOW_TITLE, FRIENDS_WINDOW_URL, FRIENDS_WINDOW_WIDTH, FRIENDS_WINDOW_HEIGHT, false); connect(_friendsWindow, &WebWindowClass::closed, this, &Application::friendsWindowClosed); } diff --git a/interface/src/Application.h b/interface/src/Application.h index a6bb507890..32d81bf44b 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -75,7 +75,12 @@ #include "octree/OctreeFade.h" #include "octree/OctreePacketProcessor.h" #include "UndoStackScriptingInterface.h" +<<<<<<< HEAD #include "DisplayPlugins.h" +======= + +#include "render/Engine.h" +>>>>>>> master class QGLWidget; class QKeyEvent; @@ -213,7 +218,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; } @@ -281,9 +285,24 @@ public: virtual void setupWorldLight(); virtual bool shouldRenderMesh(float largestDimension, float distanceToCamera); - QImage renderAvatarBillboard(); + QImage renderAvatarBillboard(RenderArgs* renderArgs); +<<<<<<< HEAD void displaySide(const Camera& camera, bool selfAvatarOnly = false); +======= + 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. + void updateUntranslatedViewMatrix(const glm::vec3& viewMatrixTranslation = glm::vec3()); + + const glm::mat4& getUntranslatedViewMatrix() const { return _untranslatedViewMatrix; } + + /// Loads a view matrix that incorporates the specified model translation without the precision issues that can + /// result from matrix multiplication at high translation magnitudes. + void loadTranslatedViewMatrix(const glm::vec3& translation); + +>>>>>>> master void getModelViewMatrix(glm::dmat4* modelViewMatrix); void getProjectionMatrix(glm::dmat4* projectionMatrix); @@ -353,6 +372,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: @@ -472,7 +496,9 @@ private slots: void setCursorVisible(bool visible); private: - void updateCursorVisibility(); + void resetCamerasOnResizeGL(Camera& camera, const glm::uvec2& size); + void updateProjectionMatrix(); + void updateProjectionMatrix(Camera& camera, bool updateViewFrustum = true); void sendPingPackets(); @@ -500,8 +526,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); @@ -534,7 +560,6 @@ private: QElapsedTimer _timerStart; QElapsedTimer _lastTimeUpdated; bool _justStarted; - Stars _stars; ShapeManager _shapeManager; PhysicalEntitySimulation _entitySimulation; @@ -637,9 +662,6 @@ private: TouchEvent _lastTouchEvent; - Overlays _overlays; - ApplicationOverlay _applicationOverlay; - RunningScriptsWidget* _runningScriptsWidget; QHash _scriptEnginesHash; bool _runningScriptsWidgetWasVisible; @@ -675,6 +697,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 diff --git a/interface/src/Environment.h b/interface/src/Environment.h index fac5aa26ca..5260b979c5 100644 --- a/interface/src/Environment.h +++ b/interface/src/Environment.h @@ -19,7 +19,7 @@ #include "EnvironmentData.h" -class Camera; +class ViewFrustum; class ProgramObject; class Environment { diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp index cfc1c94995..79d49fd6f1 100644 --- a/interface/src/LODManager.cpp +++ b/interface/src/LODManager.cpp @@ -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 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::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) { diff --git a/interface/src/LODManager.h b/interface/src/LODManager.h index 615bcec24d..98ababbda0 100644 --- a/interface/src/LODManager.h +++ b/interface/src/LODManager.h @@ -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); diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 1f217d92b9..04d83fcf3e 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -33,45 +33,44 @@ using namespace std; -void renderWorldBox() { +void renderWorldBox(gpu::Batch& batch) { auto geometryCache = DependencyManager::get(); // 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 diff --git a/interface/src/Util.h b/interface/src/Util.h index 419b776706..ed05209747 100644 --- a/interface/src/Util.h +++ b/interface/src/Util.h @@ -16,10 +16,12 @@ #include #include +#include + 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, diff --git a/interface/src/audio/AudioToolBox.cpp b/interface/src/audio/AudioToolBox.cpp index 85b8b19788..68328e151e 100644 --- a/interface/src/audio/AudioToolBox.cpp +++ b/interface/src/audio/AudioToolBox.cpp @@ -40,13 +40,13 @@ void AudioToolBox::render(int x, int y, int padding, bool boxed) { glEnable(GL_TEXTURE_2D); if (!_micTexture) { - _micTexture = DependencyManager::get()->getImageTexture(PathUtils::resourcesPath() + "images/mic.svg"); + _micTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/mic.svg"); } if (!_muteTexture) { - _muteTexture = DependencyManager::get()->getImageTexture(PathUtils::resourcesPath() + "images/mic-mute.svg"); + _muteTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/mic-mute.svg"); } if (_boxTexture) { - _boxTexture = DependencyManager::get()->getImageTexture(PathUtils::resourcesPath() + "images/audio-box.svg"); + _boxTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/audio-box.svg"); } auto audioIO = DependencyManager::get(); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index f5af137710..02af30b426 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #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.get())->getBounds(); + } + template <> void payloadRender(const AvatarSharedPointer& avatar, RenderArgs* args) { + Avatar* avatarPtr = static_cast(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()->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,29 @@ 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 scene, render::PendingChanges& pendingChanges) { + auto avatarPayload = new render::Payload(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 scene, render::PendingChanges& pendingChanges) { + pendingChanges.removeItem(_renderItemID); + _skeletonModel.removeFromScene(scene, pendingChanges); + getHead()->getFaceModel().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()->getMyAvatar()->getPosition(), _position) < 10.0f) { auto geometryCache = DependencyManager::get(); @@ -299,15 +339,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 +361,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 +373,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 +399,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 +430,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 +442,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()->renderSphere(LOOK_AT_INDICATOR_RADIUS, 15, 15, LOOK_AT_INDICATOR_COLOR); - glPopMatrix(); + Transform transform; + transform.setTranslation(position); + batch->setModelTransform(transform); + DependencyManager::get()->renderSphere(*batch, LOOK_AT_INDICATOR_RADIUS, 15, 15, LOOK_AT_INDICATOR_COLOR); } } @@ -437,31 +472,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()->allocateID(); } - DependencyManager::get()->renderSphere(sphereRadius, 15, 15, + + DependencyManager::get()->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 +517,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 +578,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 +605,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()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, + + gpu::Batch& batch = *renderArgs->_batch; + batch.setUniformTexture(0, _billboardTexture->getGPUTexture()); + DependencyManager::get()->bindSimpleProgram(batch, true); + DependencyManager::get()->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 +690,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()->shouldShowReceiveStats(); + bool shouldShowReceiveStats = DependencyManager::get()->shouldShowReceiveStats() && !isMyAvatar(); if ((_displayName.isEmpty() && !shouldShowReceiveStats) || _displayNameAlpha == 0.0f) { return; @@ -665,31 +702,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 +724,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()->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()->bindSimpleProgram(*batch); + DependencyManager::get()->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 +1065,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; } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 3c784fb925..dbc59f7d9c 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -20,12 +20,20 @@ #include #include +#include + #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 Payload; + typedef std::shared_ptr 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 scene, + render::PendingChanges& pendingChanges); + + void removeFromScene(AvatarSharedPointer self, std::shared_ptr 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(_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; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 3c8aad3f5d..f16173b79e 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -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 >("NodeWeakPointer"); - _myAvatar = QSharedPointer(new MyAvatar()); + _myAvatar = std::make_shared(); } 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,7 +97,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { // simulate avatars AvatarHash::iterator avatarIterator = _avatarHash.begin(); while (avatarIterator != _avatarHash.end()) { - Avatar* avatar = reinterpret_cast(avatarIterator.value().data()); + auto avatar = std::dynamic_pointer_cast(avatarIterator.value()); if (avatar == _myAvatar || !avatar->isInitialized()) { // DO NOT update _myAvatar! Its update has already been done earlier in the main loop. @@ -111,69 +116,39 @@ 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()->shouldRenderAvatars()) { - foreach (const AvatarSharedPointer& avatarPointer, _avatarHash) { - Avatar* avatar = static_cast(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::iterator fadingIterator = _avatarFades.begin(); const float SHRINK_RATE = 0.9f; const float MIN_FADE_SCALE = 0.001f; - + + render::ScenePointer scene = Application::getInstance()->getMain3DScene(); + render::PendingChanges pendingChanges; while (fadingIterator != _avatarFades.end()) { - Avatar* avatar = static_cast(fadingIterator->data()); + auto avatar = std::static_pointer_cast(*fadingIterator); avatar->setTargetScale(avatar->getScale() * SHRINK_RATE, true); if (avatar->getTargetScale() < MIN_FADE_SCALE) { + avatar->removeFromScene(*fadingIterator, scene, pendingChanges); fadingIterator = _avatarFades.erase(fadingIterator); } else { avatar->simulate(deltaTime); ++fadingIterator; } } -} - -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(fadingAvatar.data()); - if (avatar != static_cast(_myAvatar.data()) && avatar->isInitialized()) { - avatar->render(cameraPosition, renderMode); - } - } + scene->enqueuePendingChanges(pendingChanges); } AvatarSharedPointer AvatarManager::newSharedAvatar() { - return AvatarSharedPointer(new Avatar()); + return AvatarSharedPointer(std::make_shared()); } // virtual AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { - AvatarSharedPointer avatar = AvatarHashMap::addAvatar(sessionUUID, mixerWeakPointer); + auto avatar = std::dynamic_pointer_cast(AvatarHashMap::addAvatar(sessionUUID, mixerWeakPointer)); + render::ScenePointer scene = Application::getInstance()->getMain3DScene(); + render::PendingChanges pendingChanges; + avatar->addToScene(avatar, scene, pendingChanges); + scene->enqueuePendingChanges(pendingChanges); return avatar; } @@ -194,10 +169,9 @@ 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(avatarIterator.value().data()); + std::shared_ptr avatar = std::dynamic_pointer_cast(avatarIterator.value()); if (avatar != _myAvatar && avatar->isInitialized()) { - removeAvatarMotionState(avatar); - + removeAvatarMotionState(avatar.get()); _avatarFades.push_back(avatarIterator.value()); _avatarHash.erase(avatarIterator); } @@ -208,12 +182,12 @@ 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(avatarIterator.value().data()); + auto avatar = std::static_pointer_cast(avatarIterator.value()); if (avatar == _myAvatar || !avatar->isInitialized()) { // don't remove myAvatar or uninitialized avatars from the list ++avatarIterator; } else { - removeAvatarMotionState(avatar); + removeAvatarMotionState(avatar.get()); _avatarFades.push_back(avatarIterator.value()); avatarIterator = _avatarHash.erase(avatarIterator); } @@ -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(avatarItr.value().data()); + auto avatar = std::static_pointer_cast(avatarItr.value()); AvatarMotionState* motionState = avatar->_motionState; if (motionState) { motionState->addDirtyFlags(EntityItem::DIRTY_SHAPE); @@ -285,7 +259,7 @@ void AvatarManager::updateAvatarPhysicsShape(const QUuid& id) { avatar->computeShapeInfo(shapeInfo); btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo); if (shape) { - AvatarMotionState* motionState = new AvatarMotionState(avatar, shape); + AvatarMotionState* motionState = new AvatarMotionState(avatar.get(), shape); avatar->_motionState = motionState; _motionStatesToAdd.insert(motionState); _avatarMotionStates.insert(motionState); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 51eb3a8ec2..b4482c5a34 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -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 _avatarFades; - QSharedPointer _myAvatar; + std::shared_ptr _myAvatar; quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate. QVector _localLights; diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index 722f998f86..1501c52de5 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -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); diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index e67ed15b67..74653d9768 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -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); } diff --git a/interface/src/avatar/Hand.h b/interface/src/avatar/Hand.h index e19f5674b4..cb35497960 100644 --- a/interface/src/avatar/Hand.h +++ b/interface/src/avatar/Hand.h @@ -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); diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 8691243c07..0982a33826 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -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(); - DependencyManager::get()->begin(); + DependencyManager::get()->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()->end(); + DependencyManager::get()->end(renderArgs); } diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index 6eba16824e..a208574c26 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -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); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e573110157..8a49d69129 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -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()->getAvatarHash()) { - Avatar* avatar = static_cast(avatarPointer.data()); + Avatar* avatar = static_cast(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(_lookAtTargetAvatar.data())->setIsLookAtTarget(true); + auto avatarPointer = _lookAtTargetAvatar.lock(); + if (avatarPointer) { + static_cast(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 diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 4746a40099..c8d16e8cb0 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -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 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 _lookAtTargetAvatar; + AvatarWeakPointer _lookAtTargetAvatar; glm::vec3 _targetAvatarPosition; bool _shouldRender; bool _billboardValid; diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 116e0d8c57..e1046cd33e 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -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) { diff --git a/interface/src/devices/CameraToolBox.cpp b/interface/src/devices/CameraToolBox.cpp index a1e00d7052..27cee5185b 100644 --- a/interface/src/devices/CameraToolBox.cpp +++ b/interface/src/devices/CameraToolBox.cpp @@ -76,10 +76,10 @@ void CameraToolBox::render(int x, int y, bool boxed) { glEnable(GL_TEXTURE_2D); if (!_enabledTexture) { - _enabledTexture = DependencyManager::get()->getImageTexture(PathUtils::resourcesPath() + "images/face.svg"); + _enabledTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/face.svg"); } if (!_mutedTexture) { - _mutedTexture = DependencyManager::get()->getImageTexture(PathUtils::resourcesPath() + "images/face-mute.svg"); + _mutedTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/face-mute.svg"); } const int MUTE_ICON_SIZE = 24; diff --git a/interface/src/devices/MotionTracker.h b/interface/src/devices/MotionTracker.h index 5dd3f1ca3f..edfd3ae991 100644 --- a/interface/src/devices/MotionTracker.h +++ b/interface/src/devices/MotionTracker.h @@ -14,7 +14,18 @@ #include "DeviceTracker.h" +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-compare" +#endif + #include + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + + #include #include diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 78fe4508f3..b5538dd125 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -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()->prepare(); + DependencyManager::get()->prepare(renderArgs); } else { auto primaryFBO = DependencyManager::get()->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()->render(); + finalFbo = DependencyManager::get()->render(renderArgs); } else { finalFbo = DependencyManager::get()->getPrimaryFramebuffer(); glBindFramebuffer(GL_FRAMEBUFFER, 0); diff --git a/interface/src/devices/OculusManager.h b/interface/src/devices/OculusManager.h index 79b86e1a93..d65c31335b 100644 --- a/interface/src/devices/OculusManager.h +++ b/interface/src/devices/OculusManager.h @@ -21,6 +21,8 @@ #include #include +#include "RenderArgs.h" + class Camera; class PalmData; class Text3DOverlay; @@ -62,7 +64,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 diff --git a/interface/src/devices/TV3DManager.cpp b/interface/src/devices/TV3DManager.cpp index f2de97856e..a1e58fa8dc 100644 --- a/interface/src/devices/TV3DManager.cpp +++ b/interface/src/devices/TV3DManager.cpp @@ -82,7 +82,7 @@ void TV3DManager::configureCamera(Camera& whichCamera_, int screenWidth, int scr 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 @@ -95,7 +95,7 @@ void TV3DManager::display(Camera& whichCamera) { int portalH = deviceSize.height(); - DependencyManager::get()->prepare(); + DependencyManager::get()->prepare(renderArgs); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); Camera eyeCamera; @@ -120,10 +120,9 @@ void TV3DManager::display(Camera& whichCamera) { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); - qApp->displaySide(eyeCamera, false); -#if 0 + renderArgs->_renderSide = RenderArgs::MONO; + qApp->displaySide(renderArgs, eyeCamera, false); qApp->getApplicationOverlay().displayOverlayTextureStereo(whichCamera, _aspect, fov); -#endif _activeEye = NULL; }, [&]{ // render right side view @@ -132,7 +131,7 @@ void TV3DManager::display(Camera& whichCamera) { glPopMatrix(); glDisable(GL_SCISSOR_TEST); - auto finalFbo = DependencyManager::get()->render(); + auto finalFbo = DependencyManager::get()->render(renderArgs); auto fboSize = finalFbo->getSize(); // Get the ACTUAL device size for the BLIT deviceSize = qApp->getDeviceSize(); diff --git a/interface/src/devices/TV3DManager.h b/interface/src/devices/TV3DManager.h index 63bc319d83..1a63161b1e 100644 --- a/interface/src/devices/TV3DManager.h +++ b/interface/src/devices/TV3DManager.h @@ -32,7 +32,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: diff --git a/interface/src/octree/OctreeFade.cpp b/interface/src/octree/OctreeFade.cpp index ad313bdb6d..881f3c5938 100644 --- a/interface/src/octree/OctreeFade.cpp +++ b/interface/src/octree/OctreeFade.cpp @@ -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()->begin(); +void OctreeFade::render(RenderArgs* renderArgs) { + DependencyManager::get()->begin(renderArgs); glDisable(GL_LIGHTING); glPushMatrix(); @@ -53,7 +53,7 @@ void OctreeFade::render() { glEnable(GL_LIGHTING); - DependencyManager::get()->end(); + DependencyManager::get()->end(renderArgs); opacity *= (direction == FADE_OUT) ? FADE_OUT_STEP : FADE_IN_STEP; } diff --git a/interface/src/octree/OctreeFade.h b/interface/src/octree/OctreeFade.h index daae9be66f..137a505537 100644 --- a/interface/src/octree/OctreeFade.h +++ b/interface/src/octree/OctreeFade.h @@ -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; }; diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 38581b4fa9..8c6f6aa72e 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -189,7 +189,7 @@ GLuint ApplicationOverlay::getOverlayTexture() { } // 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(); @@ -228,7 +228,7 @@ void ApplicationOverlay::renderOverlay() { // give external parties a change to hook in emit qApp->renderingOverlay(); - overlays.renderHUD(); + overlays.renderHUD(renderArgs); renderPointers(); @@ -297,9 +297,8 @@ void ApplicationOverlay::displayOverlayTexture() { glLoadIdentity(); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); - if (_alpha < 1.0) { - glEnable(GL_BLEND); - } + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glViewport(0, 0, qApp->getDeviceSize().width(), qApp->getDeviceSize().height()); static const glm::vec2 topLeft(-1, 1); @@ -307,9 +306,38 @@ void ApplicationOverlay::displayOverlayTexture() { static const glm::vec2 texCoordTopLeft(0.0f, 1.0f); static const glm::vec2 texCoordBottomRight(1.0f, 0.0f); with_each_texture(_overlays.getTexture(), _newUiTexture, [&] { - DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, + DependencyManager::get()->renderQuad( + topLeft, bottomRight, + texCoordTopLeft, texCoordBottomRight, glm::vec4(1.0f, 1.0f, 1.0f, _alpha)); }); + + if (!_crosshairTexture) { + _crosshairTexture = DependencyManager::get()-> + getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png"); + } + + //draw the mouse pointer + glm::vec2 canvasSize = qApp->getCanvasSize(); + glm::vec2 mouseSize = 32.0f / canvasSize; + auto mouseTopLeft = topLeft * mouseSize; + auto mouseBottomRight = bottomRight * mouseSize; + vec2 mousePosition = vec2(qApp->getMouseX(), qApp->getMouseY()); + mousePosition /= canvasSize; + mousePosition *= 2.0f; + mousePosition -= 1.0f; + mousePosition.y *= -1.0f; + + glEnable(GL_TEXTURE_2D); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); + glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f }; + DependencyManager::get()->renderQuad( + mouseTopLeft + mousePosition, mouseBottomRight + mousePosition, + texCoordTopLeft, texCoordBottomRight, + reticleColor); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); + glDisable(GL_TEXTURE_2D); } glPopMatrix(); } @@ -454,11 +482,14 @@ void ApplicationOverlay::displayOverlayTextureStereo(Camera& whichCamera, float }); if (!_crosshairTexture) { - _crosshairTexture = DependencyManager::get()-> - getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png"); + _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + + "images/sixense-reticle.png"); } //draw the mouse pointer + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); glm::vec2 canvasSize = qApp->getCanvasSize(); const float reticleSize = 40.0f / canvasSize.x * quadWidth; @@ -592,11 +623,12 @@ bool ApplicationOverlay::calculateRayUICollisionPoint(const glm::vec3& position, void ApplicationOverlay::renderPointers() { //lazily load crosshair texture if (_crosshairTexture == 0) { - _crosshairTexture = DependencyManager::get()-> - getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png"); + _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png"); } glEnable(GL_TEXTURE_2D); - + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); @@ -760,8 +792,14 @@ void ApplicationOverlay::renderControllerPointers() { } void ApplicationOverlay::renderPointersOculus() { + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); glDisable(GL_DEPTH_TEST); + glMatrixMode(GL_MODELVIEW); //Controller Pointers @@ -786,6 +824,8 @@ void ApplicationOverlay::renderPointersOculus() { } glEnable(GL_DEPTH_TEST); + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); } //Renders a small magnification of the currently bound texture at the coordinates diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index e40db9c326..6cd269dc26 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -35,9 +35,9 @@ public: ApplicationOverlay(); ~ApplicationOverlay(); - void renderOverlay(); + void renderOverlay(RenderArgs* renderArgs); GLuint getOverlayTexture(); - + QPoint getPalmClickLocation(const PalmData *palm) const; bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const; diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp index b452f153f0..196ba5d29e 100644 --- a/interface/src/ui/LoginDialog.cpp +++ b/interface/src/ui/LoginDialog.cpp @@ -1,6 +1,6 @@ // -// // LoginDialog.cpp +// interface/src/ui // // Created by Bradley Austin Davis on 2015/04/14 // Copyright 2015 High Fidelity, Inc. @@ -8,16 +8,22 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + #include "LoginDialog.h" -#include "DependencyManager.h" -#include "AccountManager.h" -#include "Menu.h" +#include + #include +#include "AccountManager.h" +#include "DependencyManager.h" +#include "Menu.h" + HIFI_QML_DEF(LoginDialog) -LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent), _rootUrl(NetworkingConstants::METAVERSE_SERVER_URL.toString()) { +LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent), + _rootUrl(NetworkingConstants::METAVERSE_SERVER_URL.toString()) +{ connect(&AccountManager::getInstance(), &AccountManager::loginComplete, this, &LoginDialog::handleLoginCompleted); connect(&AccountManager::getInstance(), &AccountManager::loginFailed, @@ -48,7 +54,7 @@ void LoginDialog::handleLoginCompleted(const QUrl&) { } void LoginDialog::handleLoginFailed() { - setStatusText("Invalid username or password.< / font>"); + setStatusText("Invalid username or password"); } void LoginDialog::setStatusText(const QString& statusText) { @@ -68,10 +74,11 @@ QString LoginDialog::rootUrl() const { void LoginDialog::login(const QString& username, const QString& password) { qDebug() << "Attempting to login " << username; - setStatusText("Authenticating..."); + setStatusText("Logging in..."); AccountManager::getInstance().requestAccessToken(username, password); } void LoginDialog::openUrl(const QString& url) { qDebug() << url; + QDesktopServices::openUrl(url); } diff --git a/interface/src/ui/LoginDialog.h b/interface/src/ui/LoginDialog.h index e9ae0a1c16..50c820aa07 100644 --- a/interface/src/ui/LoginDialog.h +++ b/interface/src/ui/LoginDialog.h @@ -1,5 +1,6 @@ // // LoginDialog.h +// interface/src/ui // // Created by Bradley Austin Davis on 2015/04/14 // Copyright 2015 High Fidelity, Inc. @@ -9,6 +10,7 @@ // #pragma once + #ifndef hifi_LoginDialog_h #define hifi_LoginDialog_h diff --git a/interface/src/ui/RearMirrorTools.cpp b/interface/src/ui/RearMirrorTools.cpp index ec73668d9e..33f3e1b487 100644 --- a/interface/src/ui/RearMirrorTools.cpp +++ b/interface/src/ui/RearMirrorTools.cpp @@ -34,11 +34,10 @@ RearMirrorTools::RearMirrorTools(QRect& bounds) : _windowed(false), _fullScreen(false) { - auto textureCache = DependencyManager::get(); - _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); diff --git a/interface/src/ui/RearMirrorTools.h b/interface/src/ui/RearMirrorTools.h index 2b359c2d07..c633d72a49 100644 --- a/interface/src/ui/RearMirrorTools.h +++ b/interface/src/ui/RearMirrorTools.h @@ -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 rearViewZoomLevel; diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index c49208c835..32df75c46d 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -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); } diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index 00c1650b71..4c6d6ede4e 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -14,7 +14,7 @@ #include -#include +#include 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 diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index f0c49979c8..db252d8a04 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -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); diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index bbf850da8e..b24908a0cc 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -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); diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index 60f985b083..6fc9fe6e27 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -126,7 +126,7 @@ void Cube3DOverlay::render(RenderArgs* args) { } else { glScalef(dimensions.x, dimensions.y, dimensions.z); - DependencyManager::get()->renderWireCube(1.0f, cubeColor); + DependencyManager::get()->renderWireCube(1.0f, cubeColor); } } glPopMatrix(); diff --git a/interface/src/ui/overlays/LocalModelsOverlay.cpp b/interface/src/ui/overlays/LocalModelsOverlay.cpp index 7390ea4310..e5c8e6076d 100644 --- a/interface/src/ui/overlays/LocalModelsOverlay.cpp +++ b/interface/src/ui/overlays/LocalModelsOverlay.cpp @@ -45,10 +45,10 @@ void LocalModelsOverlay::render(RenderArgs* args) { glPushMatrix(); { Application* app = Application::getInstance(); - Transform originalTransform = qApp->getViewTransform(); - qApp->getViewTransform().postTranslate(_position); - _entityTreeRenderer->render(); - qApp->setViewTransform(originalTransform); + glm::vec3 oldTranslation = app->getViewMatrixTranslation(); + app->setViewMatrixTranslation(oldTranslation + _position); + _entityTreeRenderer->render(args); + Application::getInstance()->setViewMatrixTranslation(oldTranslation); } glPopMatrix(); } } diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 749dba9ea8..822fff9808 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -9,6 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include #include "ModelOverlay.h" @@ -54,11 +55,34 @@ void ModelOverlay::update(float deltatime) { _isLoaded = _model.isActive(); } +bool ModelOverlay::addToScene(Overlay::Pointer overlay, std::shared_ptr scene, render::PendingChanges& pendingChanges) { + Base3DOverlay::addToScene(overlay, scene, pendingChanges); + _model.addToScene(scene, pendingChanges); + return true; +} + +void ModelOverlay::removeFromScene(Overlay::Pointer overlay, std::shared_ptr 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) { diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index 8cd095f778..a81cae530a 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -32,6 +32,9 @@ public: virtual ModelOverlay* createClone() const; + virtual bool addToScene(Overlay::Pointer overlay, std::shared_ptr scene, render::PendingChanges& pendingChanges); + virtual void removeFromScene(Overlay::Pointer overlay, std::shared_ptr scene, render::PendingChanges& pendingChanges); + private: Model _model; diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index 024a94e950..745c2b4a10 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -16,6 +16,7 @@ #include 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 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 scene, render::PendingChanges& pendingChanges) { + pendingChanges.removeItem(_renderItemID); +} + diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h index 9077605fc4..1a808bc15c 100644 --- a/interface/src/ui/overlays/Overlay.h +++ b/interface/src/ui/overlays/Overlay.h @@ -21,6 +21,8 @@ #include #include // for xColor #include +#include +#include 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 Pointer; + + typedef render::Payload Payload; + typedef std::shared_ptr 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 scene, render::PendingChanges& pendingChanges); + virtual void removeFromScene(Overlay::Pointer overlay, std::shared_ptr 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 diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 0537c62b38..6e579ed4c4 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #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(); - 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()->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(); - RenderArgs args(NULL, Application::getInstance()->getDisplayViewFrustum(), - lodManager->getOctreeSizeScale(), - lodManager->getBoundaryLevelAdjust(), - renderMode, renderSide, renderDebugFlags); - - - foreach(Overlay* thisOverlay, _overlaysWorld) { - Base3DOverlay* overlay3D = static_cast(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(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(thisOverlay); + Base3DOverlay* overlay3D = static_cast(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 i(_overlaysHUD); + QMapIterator 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(i.value()); + Base3DOverlay* thisOverlay = static_cast(i.value().get()); if (!thisOverlay->getIgnoreRayIntersection()) { if (thisOverlay->findRayIntersection(origin, direction, distance, thisFace)) { return thisID; } } } else { - Overlay2D* thisOverlay = static_cast(i.value()); + Overlay2D* thisOverlay = static_cast(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::max(); bool bestIsFront = false; RayToOverlayIntersectionResult result; - QMapIterator i(_overlaysWorld); + QMapIterator i(_overlaysWorld); i.toBack(); while (i.hasPrevious()) { i.previous(); unsigned int thisID = i.key(); - Base3DOverlay* thisOverlay = static_cast(i.value()); + Base3DOverlay* thisOverlay = static_cast(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(thisOverlay)->textSize(text); + return static_cast(thisOverlay.get())->textSize(text); } } else { thisOverlay = _overlaysWorld[id]; if (thisOverlay) { if (typeid(*thisOverlay) == typeid(Text3DOverlay)) { - return static_cast(thisOverlay)->textSize(text); + return static_cast(thisOverlay.get())->textSize(text); } } } diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 04e306097b..57d8ccd509 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -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 _overlaysHUD; - QMap _overlaysWorld; - QList _overlaysToDelete; + void cleanupOverlaysToDelete(); + QMap _overlaysHUD; + QMap _overlaysWorld; + QList _overlaysToDelete; unsigned int _nextOverlayID; QReadWriteLock _lock; QReadWriteLock _deleteLock; diff --git a/interface/src/ui/overlays/OverlaysPayload.cpp b/interface/src/ui/overlays/OverlaysPayload.cpp new file mode 100644 index 0000000000..cf3262d05c --- /dev/null +++ b/interface/src/ui/overlays/OverlaysPayload.cpp @@ -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 + +#include +#include + +#include +#include +#include +#include + +#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(overlay.get())->getDrawOnHUD()) { + if (static_cast(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(overlay.get())->getBounds(); + } else { + QRect bounds = static_cast(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()->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(); + } + } +} diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp index 2cf8e6d006..fd8410b7e6 100644 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -11,15 +11,12 @@ // include this before QGLWidget, which includes an earlier version of OpenGL #include "InterfaceConfig.h" -#include - #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()->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; diff --git a/interface/src/ui/overlays/Text3DOverlay.h b/interface/src/ui/overlays/Text3DOverlay.h index aefda852db..77a5c23cb9 100644 --- a/interface/src/ui/overlays/Text3DOverlay.h +++ b/interface/src/ui/overlays/Text3DOverlay.h @@ -17,8 +17,12 @@ #include #include +#include + #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; diff --git a/interface/src/ui/overlays/TextOverlay.cpp b/interface/src/ui/overlays/TextOverlay.cpp index 85e5af708c..e709bbd9fc 100644 --- a/interface/src/ui/overlays/TextOverlay.cpp +++ b/interface/src/ui/overlays/TextOverlay.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #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()->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); } diff --git a/interface/src/ui/overlays/TextOverlay.h b/interface/src/ui/overlays/TextOverlay.h index aea8487fd3..5a715ebfdf 100644 --- a/interface/src/ui/overlays/TextOverlay.h +++ b/interface/src/ui/overlays/TextOverlay.h @@ -19,6 +19,7 @@ #include #include +#include #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; diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 41c8cb9537..340ca9374d 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -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()->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); } diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index f9392c6a10..3b2c1c1ae6 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -37,6 +37,7 @@ #include #include #include +#include #include #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 _outputBufferSizeFrames; Setting::Handle _outputStarveDetectionEnabled; Setting::Handle _outputStarveDetectionPeriodMsec; // Maximum number of starves per _outputStarveDetectionPeriod before increasing buffer size Setting::Handle _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 _inputDevices; QVector _outputDevices; void checkDevices(); + + bool _hasReceivedFirstPacket = false; }; diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index ae397ba97e..912351e21a 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -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(); - + // 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(&_options.position), sizeof(_options.position)); - + // pack our orientation for injected audio int orientationOptionOffset = injectAudioPacket.size(); packetStream.writeRawData(reinterpret_cast(&_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); diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index ca39dcbdc4..0513b70bd8 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -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(); diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index 577328ee18..7dc6010f8f 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -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(fileHeader.wave.numChannels) > 2) { qCDebug(audio) << "Currently not support audio files with more than 2 channels."; } - + if (qFromLittleEndian(fileHeader.wave.bitsPerSample) != 16) { qCDebug(audio) << "Currently not supporting non 16bit audio files."; return; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index a27e4256ef..2a452f8510 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -13,6 +13,7 @@ #define hifi_AvatarData_h #include +#include /* 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 AvatarSharedPointer; +typedef std::weak_ptr AvatarWeakPointer; +typedef QHash AvatarHash; + // avatar motion behaviors const quint32 AVATAR_MOTION_KEYBOARD_MOTOR_ENABLED = 1U << 0; const quint32 AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED = 1U << 1; diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index 9204826d03..45edbf8d5a 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -16,16 +16,14 @@ #include #include +#include + #include #include #include "AvatarData.h" #include -typedef QSharedPointer AvatarSharedPointer; -typedef QWeakPointer AvatarWeakPointer; -typedef QHash AvatarHash; - class AvatarHashMap : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY diff --git a/libraries/entities-renderer/CMakeLists.txt b/libraries/entities-renderer/CMakeLists.txt index 394d156252..19cc8d8412 100644 --- a/libraries/entities-renderer/CMakeLists.txt +++ b/libraries/entities-renderer/CMakeLists.txt @@ -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) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 7fda8214bb..98cbc1f845 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -10,6 +10,7 @@ // #include +#include #include @@ -30,8 +31,11 @@ #include #include + #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 zone) { + QSharedPointer scene = DependencyManager::get(); + 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()->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::max(); - _tree->recurseTreeWithOperation(renderOperation, &args); - - QSharedPointer scene = DependencyManager::get(); - 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()->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(); + 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()->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()->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()->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()->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()->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()->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()->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()->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()->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(); + 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()->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()->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()->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()->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(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(_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); } } - diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 725f304e8b..e491524c78 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -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 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 _entitiesInScene; }; + #endif // hifi_EntityTreeRenderer_h diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp index 926c3f1289..b2400b797e 100644 --- a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -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()->renderWireCube(1.0f, cubeColor); - } else { - DependencyManager::get()->renderSolidCube(1.0f, cubeColor); - } - glPopMatrix(); - glPopMatrix(); + + Q_ASSERT(args->_batch); + gpu::Batch& batch = *args->_batch; + batch.setModelTransform(getTransformToCenter()); + if (highlightSimulationOwnership) { + DependencyManager::get()->renderWireCube(batch, 1.0f, cubeColor); + } else { + DependencyManager::get()->renderSolidCube(batch, 1.0f, cubeColor); + } RenderableDebugableEntityItem::render(this, args); }; diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.h b/libraries/entities-renderer/src/RenderableBoxEntityItem.h index 6ae76b0315..06a62706b9 100644 --- a/libraries/entities-renderer/src/RenderableBoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderableBoxEntityItem.h @@ -14,6 +14,7 @@ #include #include "RenderableDebugableEntityItem.h" +#include "RenderableEntityItem.h" class RenderableBoxEntityItem : public BoxEntityItem { public: @@ -24,6 +25,8 @@ public: { } virtual void render(RenderArgs* args); + + SIMPLE_RENDERABLE() }; diff --git a/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp b/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp index 7854aee82f..ca81ae4f2b 100644 --- a/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp @@ -12,7 +12,10 @@ #include + #include +#include + #include #include @@ -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()->renderWireCube(1.0f + puffedOut, color); - glPopMatrix(); - glPopMatrix(); + Q_ASSERT(args->_batch); + gpu::Batch& batch = *args->_batch; + batch.setModelTransform(entity->getTransformToCenter()); + DependencyManager::get()->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()->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()->renderSolidSphere(batch, 0.5f, SLICES, STACKS, blueColor); } void RenderableDebugableEntityItem::render(EntityItem* entity, RenderArgs* args) { diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp new file mode 100644 index 0000000000..461b64a9e6 --- /dev/null +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -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); + } + } + } +} + + + diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h new file mode 100644 index 0000000000..2037788ace --- /dev/null +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -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 +#include + + +class RenderableEntityItemProxy { +public: + RenderableEntityItemProxy(EntityItemPointer entity) : entity(entity) { } + typedef render::Payload 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 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 scene, render::PendingChanges& pendingChanges) { + pendingChanges.removeItem(_myItem); + } + +private: + render::ItemID _myItem; +}; + + +#define SIMPLE_RENDERABLE() \ +public: \ + virtual bool addToScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { return _renderHelper.addToScene(self, scene, pendingChanges); } \ + virtual void removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { _renderHelper.removeFromScene(self, scene, pendingChanges); } \ +private: \ + SimpleRenderableEntityItem _renderHelper; + + + +#endif // hifi_RenderableEntityItem_h diff --git a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp index 779de166b7..819989d5ec 100644 --- a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -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()->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()->renderWireSphere(0.5f, 15, 15, color); - glPopMatrix(); - glPopMatrix(); + Q_ASSERT(args->_batch); + gpu::Batch& batch = *args->_batch; + batch.setModelTransform(getTransformToCenter()); + DependencyManager::get()->renderWireSphere(batch, 0.5f, 15, 15, glm::vec4(color, 1.0f)); #endif }; diff --git a/libraries/entities-renderer/src/RenderableLightEntityItem.h b/libraries/entities-renderer/src/RenderableLightEntityItem.h index 427557432c..a0c424e240 100644 --- a/libraries/entities-renderer/src/RenderableLightEntityItem.h +++ b/libraries/entities-renderer/src/RenderableLightEntityItem.h @@ -13,6 +13,7 @@ #define hifi_RenderableLightEntityItem_h #include +#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(); }; diff --git a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp index 5a37afca45..4e5de331bb 100644 --- a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -23,32 +24,34 @@ EntityItemPointer RenderableLineEntityItem::factory(const EntityItemID& entityID return EntityItemPointer(new RenderableLineEntityItem(entityID, properties)); } +void RenderableLineEntityItem::updateGeometry() { + auto geometryCache = DependencyManager::get(); + 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(); - 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()->renderVertices(batch, gpu::LINE_STRIP, _lineVerticesID); + } + batch._glLineWidth(1.0f); + RenderableDebugableEntityItem::render(this, args); }; diff --git a/libraries/entities-renderer/src/RenderableLineEntityItem.h b/libraries/entities-renderer/src/RenderableLineEntityItem.h index ceb520c351..09f98ca364 100644 --- a/libraries/entities-renderer/src/RenderableLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderableLineEntityItem.h @@ -14,6 +14,7 @@ #include #include "RenderableDebugableEntityItem.h" +#include "RenderableEntityItem.h" #include class RenderableLineEntityItem : public LineEntityItem { @@ -27,7 +28,11 @@ public: virtual void render(RenderArgs* args); + SIMPLE_RENDERABLE(); + protected: + void updateGeometry(); + int _lineVerticesID; }; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 35309b0799..e0bc493a5c 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -15,9 +15,11 @@ #include +#include #include #include #include +#include #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(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 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 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 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; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index f504019e67..ac1d4b494f 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -43,6 +43,11 @@ public: virtual void somethingChangedNotification() { _needsInitialSimulation = true; } + virtual bool readyToAddToScene(RenderArgs* renderArgs = nullptr); + virtual bool addToScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges); + virtual void removeFromScene(EntityItemPointer self, std::shared_ptr 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> _points; + + render::ItemID _myMetaItem; }; #endif // hifi_RenderableModelEntityItem_h diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index f067a35514..6a50cbf1cb 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -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()->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 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()->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()->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 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 vertices(getLivingParticleCount() * VERTS_PER_PARTICLE); - QVector 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 vertices; + QVector positions; + QVector 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()->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()->renderVertices(gpu::QUADS, _cacheID); - - glPopMatrix(); - - glPopMatrix(); - - glDisable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, 0); + + if (textured) { + DependencyManager::get()->updateVertices(_cacheID, vertices, textureCoords, particleColor); + } else { + DependencyManager::get()->updateVertices(_cacheID, vertices, particleColor); + } } diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h index 6ec631d2e1..4ecea45ad0 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h @@ -13,6 +13,7 @@ #include #include +#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: diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 83612f3fce..bb5932d70c 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -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(); normal != normalAttrib.end(); 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()->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)); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index c8f1b4a49d..77aeb24f2a 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -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 diff --git a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp index 09028f09fa..d5cb7d11b8 100644 --- a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -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()->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()->renderSolidSphere(batch, 0.5f, SLICES, STACKS, sphereColor); }; diff --git a/libraries/entities-renderer/src/RenderableSphereEntityItem.h b/libraries/entities-renderer/src/RenderableSphereEntityItem.h index 3b02541061..6331b35706 100644 --- a/libraries/entities-renderer/src/RenderableSphereEntityItem.h +++ b/libraries/entities-renderer/src/RenderableSphereEntityItem.h @@ -14,6 +14,8 @@ #include +#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(); }; diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index f66724a214..8a67bb99f2 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -16,64 +16,44 @@ #include #include #include -#include #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()->bindSimpleProgram(); - DependencyManager::get()->renderQuad(topLeft, bottomRight, glm::vec4(toGlm(getBackgroundColorX()), alpha)); - DependencyManager::get()->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()->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); } diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.h b/libraries/entities-renderer/src/RenderableTextEntityItem.h index 0cf0622af5..de1d745875 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.h +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.h @@ -13,6 +13,11 @@ #define hifi_RenderableTextEntityItem_h #include +#include + +#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); }; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 74405ba586..94f88b8390 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -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()->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()->bindSimpleProgram(batch, true); + DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f)); + DependencyManager::get()->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] { diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 559392367c..47e847a166 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -13,6 +13,8 @@ #include +#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; diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index 69075b178b..9c4f8ae0bb 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -11,6 +11,9 @@ #include "RenderableZoneEntityItem.h" +#include +#include + #include #include #include @@ -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(); - - 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(); + + 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: diff --git a/libraries/entities/CMakeLists.txt b/libraries/entities/CMakeLists.txt index d21906fa3f..926fd8b4b2 100644 --- a/libraries/entities/CMakeLists.txt +++ b/libraries/entities/CMakeLists.txt @@ -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) diff --git a/libraries/entities/src/BoxEntityItem.cpp b/libraries/entities/src/BoxEntityItem.cpp index fcf37ed695..291e350db0 100644 --- a/libraries/entities/src/BoxEntityItem.cpp +++ b/libraries/entities/src/BoxEntityItem.cpp @@ -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); } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 17d0412987..4a10aad95d 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -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; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index a32f3f17a7..77a6627853 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -12,6 +12,7 @@ #ifndef hifi_EntityItem_h #define hifi_EntityItem_h +#include #include #include @@ -22,6 +23,7 @@ #include // for OctreeElement::AppendState #include #include +#include #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 scene, + render::PendingChanges& pendingChanges) { return false; } // by default entity items don't add to scene + virtual void removeFromScene(EntityItemPointer self, std::shared_ptr 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 diff --git a/libraries/entities/src/EntityItemPropertiesDefaults.h b/libraries/entities/src/EntityItemPropertiesDefaults.h index c3dea91daa..11341b160c 100644 --- a/libraries/entities/src/EntityItemPropertiesDefaults.h +++ b/libraries/entities/src/EntityItemPropertiesDefaults.h @@ -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); diff --git a/libraries/entities/src/LightEntityItem.cpp b/libraries/entities/src/LightEntityItem.cpp index 4ae9f41964..5e765a0792 100644 --- a/libraries/entities/src/LightEntityItem.cpp +++ b/libraries/entities/src/LightEntityItem.cpp @@ -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)); } } diff --git a/libraries/entities/src/LineEntityItem.cpp b/libraries/entities/src/LineEntityItem.cpp index 0c6b73a7ce..e7e1c90b41 100644 --- a/libraries/entities/src/LineEntityItem.cpp +++ b/libraries/entities/src/LineEntityItem.cpp @@ -86,6 +86,7 @@ bool LineEntityItem::setProperties(const EntityItemProperties& properties) { void LineEntityItem::setLinePoints(const QVector& points) { QVector 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& 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); } diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index 2d5c058bb8..5c8a5b63a1 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -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); } diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index e0cd397594..95ab7d1035 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -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); } diff --git a/libraries/entities/src/SphereEntityItem.cpp b/libraries/entities/src/SphereEntityItem.cpp index 2d0c69b978..2298035854 100644 --- a/libraries/entities/src/SphereEntityItem.cpp +++ b/libraries/entities/src/SphereEntityItem.cpp @@ -120,8 +120,8 @@ void SphereEntityItem::debugDump() const { quint64 now = usecTimestampNow(); qCDebug(entities) << "SHPERE 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); } diff --git a/libraries/entities/src/TextEntityItem.cpp b/libraries/entities/src/TextEntityItem.cpp index 920f67fd57..5718fe8c12 100644 --- a/libraries/entities/src/TextEntityItem.cpp +++ b/libraries/entities/src/TextEntityItem.cpp @@ -44,7 +44,7 @@ const float TEXT_ENTITY_ITEM_FIXED_DEPTH = 0.01f; void TextEntityItem::setDimensions(const glm::vec3& value) { // NOTE: Text Entities always have a "depth" of 1cm. - _dimensions = glm::vec3(value.x, value.y, TEXT_ENTITY_ITEM_FIXED_DEPTH); + EntityItem::setDimensions(glm::vec3(value.x, value.y, TEXT_ENTITY_ITEM_FIXED_DEPTH)); } EntityItemProperties TextEntityItem::getProperties() const { @@ -135,7 +135,7 @@ bool TextEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const PlaneShape plane; const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f); - glm::vec3 normal = _rotation * UNROTATED_NORMAL; + glm::vec3 normal = getRotation() * UNROTATED_NORMAL; plane.setNormal(normal); plane.setPoint(getPosition()); // the position is definitely a point on our plane diff --git a/libraries/entities/src/WebEntityItem.cpp b/libraries/entities/src/WebEntityItem.cpp index 981be4b667..4c849f5270 100644 --- a/libraries/entities/src/WebEntityItem.cpp +++ b/libraries/entities/src/WebEntityItem.cpp @@ -38,7 +38,7 @@ const float WEB_ENTITY_ITEM_FIXED_DEPTH = 0.01f; void WebEntityItem::setDimensions(const glm::vec3& value) { // NOTE: Web Entities always have a "depth" of 1cm. - _dimensions = glm::vec3(value.x, value.y, WEB_ENTITY_ITEM_FIXED_DEPTH); + EntityItem::setDimensions(glm::vec3(value.x, value.y, WEB_ENTITY_ITEM_FIXED_DEPTH)); } EntityItemProperties WebEntityItem::getProperties() const { @@ -112,7 +112,7 @@ bool WebEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const g PlaneShape plane; const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f); - glm::vec3 normal = _rotation * UNROTATED_NORMAL; + glm::vec3 normal = getRotation() * UNROTATED_NORMAL; plane.setNormal(normal); plane.setPoint(getPosition()); // the position is definitely a point on our plane diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index 1b6d12ddaf..6f44223892 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -217,8 +217,8 @@ void ZoneEntityItem::debugDump() const { quint64 now = usecTimestampNow(); qCDebug(entities) << " ZoneEntityItem id:" << getEntityItemID() << "---------------------------------------------"; qCDebug(entities) << " keyLightColor:" << _keyLightColor[0] << "," << _keyLightColor[1] << "," << _keyLightColor[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); qCDebug(entities) << " _keyLightIntensity:" << _keyLightIntensity; qCDebug(entities) << " _keyLightAmbientIntensity:" << _keyLightAmbientIntensity; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 464deb1059..f282f7e35d 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1902,8 +1902,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, } else { material._material->setDiffuse(material.diffuse); } - material._material->setSpecular(material.specular); - material._material->setShininess(material.shininess); + material._material->setMetallic(glm::length(material.specular)); + material._material->setGloss(material.shininess); if (material.opacity <= 0.0f) { material._material->setOpacity(1.0f); diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 4a8a2fc53d..080fb98690 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -134,8 +134,8 @@ void setMeshPartDefaults(FBXMeshPart& meshPart, QString materialID) { meshPart._material = model::MaterialPointer(new model::Material()); meshPart._material->setDiffuse(glm::vec3(1.0, 1.0, 1.0)); meshPart._material->setOpacity(1.0); - meshPart._material->setSpecular(glm::vec3(1.0, 1.0, 1.0)); - meshPart._material->setShininess(96.0); + meshPart._material->setMetallic(0.0); + meshPart._material->setGloss(96.0); meshPart._material->setEmissive(glm::vec3(0.0, 0.0, 0.0)); } @@ -481,8 +481,8 @@ FBXGeometry OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping, Q meshPart.specularTexture.filename = material->specularTextureFilename; // ... and some things are set in the underlying material. meshPart._material->setDiffuse(material->diffuseColor); - meshPart._material->setSpecular(material->specularColor); - meshPart._material->setShininess(material->shininess); + meshPart._material->setMetallic(glm::length(material->specularColor)); + meshPart._material->setGloss(material->shininess); meshPart._material->setOpacity(material->opacity); } // qCDebug(modelformat) << "OBJ Reader part:" << meshPartCount << "name:" << leadFace.groupName << "material:" << groupMaterialName << "diffuse:" << meshPart._material->getDiffuse() << "faces:" << faceGroup.count() << "triangle indices will start with:" << mesh.vertices.count(); @@ -567,10 +567,10 @@ void fbxDebugDump(const FBXGeometry& fbxgeo) { qCDebug(modelformat) << " quadIndices.count() =" << meshPart.quadIndices.count(); qCDebug(modelformat) << " triangleIndices.count() =" << meshPart.triangleIndices.count(); qCDebug(modelformat) << " diffuseColor =" << meshPart.diffuseColor << "mat =" << meshPart._material->getDiffuse(); - qCDebug(modelformat) << " specularColor =" << meshPart.specularColor << "mat =" << meshPart._material->getSpecular(); + qCDebug(modelformat) << " specularColor =" << meshPart.specularColor << "mat =" << meshPart._material->getMetallic(); qCDebug(modelformat) << " emissiveColor =" << meshPart.emissiveColor << "mat =" << meshPart._material->getEmissive(); qCDebug(modelformat) << " emissiveParams =" << meshPart.emissiveParams; - qCDebug(modelformat) << " shininess =" << meshPart.shininess << "mat =" << meshPart._material->getShininess(); + qCDebug(modelformat) << " gloss =" << meshPart.shininess << "mat =" << meshPart._material->getGloss(); qCDebug(modelformat) << " opacity =" << meshPart.opacity << "mat =" << meshPart._material->getOpacity(); qCDebug(modelformat) << " materialID =" << meshPart.materialID; qCDebug(modelformat) << " diffuse texture =" << meshPart.diffuseTexture.filename; diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index b369c05646..405ea9459d 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -143,7 +143,8 @@ public: void _glBindTexture(GLenum target, GLuint texture); void _glActiveTexture(GLenum texture); - + void _glTexParameteri(GLenum target, GLenum pname, GLint param); + void _glDrawBuffers(GLsizei n, const GLenum* bufs); void _glUseProgram(GLuint program); @@ -156,6 +157,7 @@ public: void _glDisableVertexAttribArray(GLint location); void _glColor4f(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); + void _glLineWidth(GLfloat width); enum Command { COMMAND_draw = 0, @@ -201,6 +203,7 @@ public: COMMAND_glBindTexture, COMMAND_glActiveTexture, + COMMAND_glTexParameteri, COMMAND_glDrawBuffers, @@ -214,6 +217,7 @@ public: COMMAND_glDisableVertexAttribArray, COMMAND_glColor4f, + COMMAND_glLineWidth, NUM_COMMANDS, }; diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index 54387e8f71..9cc6bb3cd7 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -15,9 +15,27 @@ using namespace gpu; +Context::Context(Backend* backend) : + _backend(backend) { +} + +Context::Context(const Context& context) { +} + +Context::~Context() { +} + bool Context::makeProgram(Shader& shader, const Shader::BindingSet& bindings) { if (shader.isProgram()) { return GLBackend::makeProgram(shader, bindings); } return false; +} + +void Context::render(Batch& batch) { + _backend->render(batch); +} + +void Context::syncCache() { + _backend->syncCache(); } \ No newline at end of file diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index 7ffb22f1c8..98ddc7fb64 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -13,6 +13,8 @@ #include +#include "Batch.h" + #include "Resource.h" #include "Texture.h" #include "Pipeline.h" @@ -20,17 +22,13 @@ namespace gpu { -class GPUObject { -public: - GPUObject() {} - virtual ~GPUObject() {} -}; - -class Batch; - class Backend { public: + virtual~ Backend() {}; + virtual void render(Batch& batch) = 0; + virtual void syncCache() = 0; + class TransformObject { public: Mat4 _model; @@ -107,15 +105,15 @@ protected: class Context { public: - Context(); - Context(const Context& context); + Context(Backend* backend); ~Context(); - void enqueueBatch(Batch& batch); - + void render(Batch& batch); + void syncCache(); protected: + Context(const Context& context); // This function can only be called by "static Shader::makeProgram()" // makeProgramShader(...) make a program shader ready to be used in a Batch. @@ -123,6 +121,8 @@ protected: // If the shader passed is not a program, nothing happens. static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings = Shader::BindingSet()); + std::unique_ptr _backend; + friend class Shader; }; diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index 3ee32ffc56..7cf913430d 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -16,7 +16,11 @@ namespace gpu { -class GPUObject; +class GPUObject { +public: + GPUObject() {} + virtual ~GPUObject() {} +}; typedef int Stamp; diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index 08a9a39d68..da6979fb27 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -54,6 +54,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::GLBackend::do_glBindTexture), (&::gpu::GLBackend::do_glActiveTexture), + (&::gpu::GLBackend::do_glTexParameteri), (&::gpu::GLBackend::do_glDrawBuffers), @@ -65,8 +66,9 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::GLBackend::do_glEnableVertexAttribArray), (&::gpu::GLBackend::do_glDisableVertexAttribArray), - + (&::gpu::GLBackend::do_glColor4f), + (&::gpu::GLBackend::do_glLineWidth), }; GLBackend::GLBackend() : @@ -96,8 +98,11 @@ void GLBackend::render(Batch& batch) { } } -void GLBackend::renderBatch(Batch& batch) { +void GLBackend::renderBatch(Batch& batch, bool syncCache) { GLBackend backend; + if (syncCache) { + backend.syncCache(); + } backend.render(batch); } @@ -134,6 +139,13 @@ bool GLBackend::checkGLError(const char* name) { } } + +void GLBackend::syncCache() { + syncTransformStateCache(); + syncPipelineStateCache(); + syncInputStateCache(); +} + void GLBackend::do_draw(Batch& batch, uint32 paramOffset) { updateInput(); updateTransform(); @@ -371,6 +383,22 @@ void GLBackend::do_glActiveTexture(Batch& batch, uint32 paramOffset) { (void) CHECK_GL_ERROR(); } +void Batch::_glTexParameteri(GLenum target, GLenum pname, GLint param) { + ADD_COMMAND_GL(glTexParameteri); + + _params.push_back(param); + _params.push_back(pname); + _params.push_back(target); + + DO_IT_NOW(glTexParameteri, 3); +} +void GLBackend::do_glTexParameteri(Batch& batch, uint32 paramOffset) { + glTexParameteri(batch._params[paramOffset + 2]._uint, + batch._params[paramOffset + 1]._uint, + batch._params[paramOffset + 0]._int); + (void) CHECK_GL_ERROR(); +} + void Batch::_glDrawBuffers(GLsizei n, const GLenum* bufs) { ADD_COMMAND_GL(glDrawBuffers); @@ -415,6 +443,8 @@ void Batch::_glUniform1f(GLint location, GLfloat v0) { } void GLBackend::do_glUniform1f(Batch& batch, uint32 paramOffset) { if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect return; } glUniform1f( @@ -433,6 +463,11 @@ void Batch::_glUniform2f(GLint location, GLfloat v0, GLfloat v1) { DO_IT_NOW(_glUniform2f, 1); } void GLBackend::do_glUniform2f(Batch& batch, uint32 paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } glUniform2f( batch._params[paramOffset + 2]._int, batch._params[paramOffset + 1]._float, @@ -451,6 +486,11 @@ void Batch::_glUniform4fv(GLint location, GLsizei count, const GLfloat* value) { DO_IT_NOW(_glUniform4fv, 3); } void GLBackend::do_glUniform4fv(Batch& batch, uint32 paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } glUniform4fv( batch._params[paramOffset + 2]._int, batch._params[paramOffset + 1]._uint, @@ -471,6 +511,11 @@ void Batch::_glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpo DO_IT_NOW(_glUniformMatrix4fv, 4); } void GLBackend::do_glUniformMatrix4fv(Batch& batch, uint32 paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } glUniformMatrix4fv( batch._params[paramOffset + 3]._int, batch._params[paramOffset + 2]._uint, @@ -522,6 +567,18 @@ void GLBackend::do_glColor4f(Batch& batch, uint32 paramOffset) { (void) CHECK_GL_ERROR(); } +void Batch::_glLineWidth(GLfloat width) { + ADD_COMMAND_GL(glLineWidth); + + _params.push_back(width); + + DO_IT_NOW(_glLineWidth, 1); +} +void GLBackend::do_glLineWidth(Batch& batch, uint32 paramOffset) { + glLineWidth(batch._params[paramOffset]._float); + (void) CHECK_GL_ERROR(); +} + void GLBackend::loadMatrix(GLenum target, const glm::mat4 & m) { glMatrixMode(target); glLoadMatrixf(glm::value_ptr(m)); @@ -547,4 +604,3 @@ void GLBackend::fetchMatrix(GLenum target, glm::mat4 & m) { } - diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index 3551953998..e7a5e62df8 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -24,12 +24,26 @@ namespace gpu { class GLBackend : public Backend { public: + explicit GLBackend(bool syncCache); GLBackend(); - ~GLBackend(); + virtual ~GLBackend(); - void render(Batch& batch); + virtual void render(Batch& batch); - static void renderBatch(Batch& batch); + // This call synchronize the Full Backend cache with the current GLState + // THis is only intended to be used when mixing raw gl calls with the gpu api usage in order to sync + // the gpu::Backend state with the true gl state which has probably been messed up by these ugly naked gl calls + // Let's try to avoid to do that as much as possible! + virtual void syncCache(); + + // Render Batch create a local Context and execute the batch with it + // WARNING: + // if syncCache is true, then the gpu::GLBackend will synchornize + // its cache with the current gl state and it's BAD + // If you know you don't rely on any state changed by naked gl calls then + // leave to false where it belongs + // if true, the needed resync IS EXPENSIVE + static void renderBatch(Batch& batch, bool syncCache = false); static bool checkGLError(const char* name = nullptr); @@ -70,8 +84,13 @@ public: GLuint _shader; GLuint _program; - GLuint _transformCameraSlot = -1; - GLuint _transformObjectSlot = -1; + GLint _transformCameraSlot = -1; + GLint _transformObjectSlot = -1; + +#if (GPU_TRANSFORM_PROFILE == GPU_CORE) +#else + GLuint _transformCamera_viewInverse = -1; +#endif GLShader(); ~GLShader(); @@ -79,6 +98,7 @@ public: static GLShader* syncGPUObject(const Shader& shader); static GLuint getShaderID(const ShaderPointer& shader); + // FIXME: Please remove these 2 calls once the text renderer doesn't use naked gl calls anymore static void loadMatrix(GLenum target, const glm::mat4 & m); static void fetchMatrix(GLenum target, glm::mat4 & m); @@ -200,7 +220,9 @@ protected: void do_setInputFormat(Batch& batch, uint32 paramOffset); void do_setInputBuffer(Batch& batch, uint32 paramOffset); void do_setIndexBuffer(Batch& batch, uint32 paramOffset); - + + // Synchronize the state cache of this Backend with the actual real state of the GL Context + void syncInputStateCache(); void updateInput(); struct InputStageState { bool _invalidFormat; @@ -238,9 +260,11 @@ protected: void do_setModelTransform(Batch& batch, uint32 paramOffset); void do_setViewTransform(Batch& batch, uint32 paramOffset); void do_setProjectionTransform(Batch& batch, uint32 paramOffset); - + void initTransform(); void killTransform(); + // Synchronize the state cache of this Backend with the actual real state of the GL Context + void syncTransformStateCache(); void updateTransform(); struct TransformStageState { TransformObject _transformObject; @@ -268,14 +292,18 @@ protected: _lastMode(GL_TEXTURE) {} } _transform; - // Pipeline Stage - void do_setPipeline(Batch& batch, uint32 paramOffset); - - void do_setStateBlendFactor(Batch& batch, uint32 paramOffset); - + // Uniform Stage void do_setUniformBuffer(Batch& batch, uint32 paramOffset); void do_setUniformTexture(Batch& batch, uint32 paramOffset); - + + struct UniformStageState { + + }; + + // Pipeline Stage + void do_setPipeline(Batch& batch, uint32 paramOffset); + void do_setStateBlendFactor(Batch& batch, uint32 paramOffset); + // Standard update pipeline check that the current Program and current State or good to go for a void updatePipeline(); // Force to reset all the state fields indicated by the 'toBeReset" signature @@ -292,12 +320,16 @@ protected: GLuint _program; bool _invalidProgram; +#if (GPU_TRANSFORM_PROFILE == GPU_CORE) +#else + GLint _program_transformCamera_viewInverse = -1; +#endif + State::Data _stateCache; State::Signature _stateSignatureCache; GLState* _state; bool _invalidState = false; - bool _needStateSync = true; PipelineStageState() : _pipeline(), @@ -306,8 +338,7 @@ protected: _stateCache(State::DEFAULT), _stateSignatureCache(0), _state(nullptr), - _invalidState(false), - _needStateSync(true) + _invalidState(false) {} } _pipeline; @@ -341,6 +372,7 @@ protected: void do_glBindTexture(Batch& batch, uint32 paramOffset); void do_glActiveTexture(Batch& batch, uint32 paramOffset); + void do_glTexParameteri(Batch& batch, uint32 paramOffset); void do_glDrawBuffers(Batch& batch, uint32 paramOffset); @@ -352,8 +384,9 @@ protected: void do_glEnableVertexAttribArray(Batch& batch, uint32 paramOffset); void do_glDisableVertexAttribArray(Batch& batch, uint32 paramOffset); - + void do_glColor4f(Batch& batch, uint32 paramOffset); + void do_glLineWidth(Batch& batch, uint32 paramOffset); typedef void (GLBackend::*CommandCall)(Batch&, uint32); static CommandCall _commandCalls[Batch::NUM_COMMANDS]; diff --git a/libraries/gpu/src/gpu/GLBackendInput.cpp b/libraries/gpu/src/gpu/GLBackendInput.cpp index fcaa28aaaa..aac7b56bc2 100755 --- a/libraries/gpu/src/gpu/GLBackendInput.cpp +++ b/libraries/gpu/src/gpu/GLBackendInput.cpp @@ -46,6 +46,12 @@ static const GLenum attributeSlotToClassicAttribName[NUM_CLASSIC_ATTRIBS] = { }; #endif +void GLBackend::syncInputStateCache() { + for (int i = 0; i < NUM_CLASSIC_ATTRIBS; i++) { + _input._attributeActivation[i] = glIsEnabled(attributeSlotToClassicAttribName[i]); + } +} + void GLBackend::updateInput() { if (_input._invalidFormat || _input._buffersState.any()) { @@ -60,23 +66,23 @@ void GLBackend::updateInput() { newActivation.set(attrib._slot); } } - + // Manage Activation what was and what is expected now for (unsigned int i = 0; i < newActivation.size(); i++) { bool newState = newActivation[i]; if (newState != _input._attributeActivation[i]) { #if defined(SUPPORT_LEGACY_OPENGL) - if (i < NUM_CLASSIC_ATTRIBS) { + const bool useClientState = i < NUM_CLASSIC_ATTRIBS; +#else + const bool useClientState = false; +#endif + if (useClientState) { if (newState) { glEnableClientState(attributeSlotToClassicAttribName[i]); - } - else { + } else { glDisableClientState(attributeSlotToClassicAttribName[i]); } } else { -#else - { -#endif if (newState) { glEnableVertexAttribArray(i); } else { @@ -84,7 +90,7 @@ void GLBackend::updateInput() { } } (void) CHECK_GL_ERROR(); - + _input._attributeActivation.flip(i); } } @@ -118,29 +124,30 @@ void GLBackend::updateInput() { GLenum type = _elementTypeToGLType[attrib._element.getType()]; GLuint stride = strides[bufferNum]; GLuint pointer = attrib._offset + offsets[bufferNum]; - #if defined(SUPPORT_LEGACY_OPENGL) - if (slot < NUM_CLASSIC_ATTRIBS) { +#if defined(SUPPORT_LEGACY_OPENGL) + const bool useClientState = slot < NUM_CLASSIC_ATTRIBS; +#else + const bool useClientState = false; +#endif + if (useClientState) { switch (slot) { - case Stream::POSITION: - glVertexPointer(count, type, stride, reinterpret_cast(pointer)); - break; - case Stream::NORMAL: - glNormalPointer(type, stride, reinterpret_cast(pointer)); - break; - case Stream::COLOR: - glColorPointer(count, type, stride, reinterpret_cast(pointer)); - break; - case Stream::TEXCOORD: - glTexCoordPointer(count, type, stride, reinterpret_cast(pointer)); - break; + case Stream::POSITION: + glVertexPointer(count, type, stride, reinterpret_cast(pointer)); + break; + case Stream::NORMAL: + glNormalPointer(type, stride, reinterpret_cast(pointer)); + break; + case Stream::COLOR: + glColorPointer(count, type, stride, reinterpret_cast(pointer)); + break; + case Stream::TEXCOORD: + glTexCoordPointer(count, type, stride, reinterpret_cast(pointer)); + break; }; } else { - #else - { - #endif GLboolean isNormalized = attrib._element.isNormalized(); glVertexAttribPointer(slot, count, type, isNormalized, stride, - reinterpret_cast(pointer)); + reinterpret_cast(pointer)); } (void) CHECK_GL_ERROR(); } diff --git a/libraries/gpu/src/gpu/GLBackendPipeline.cpp b/libraries/gpu/src/gpu/GLBackendPipeline.cpp index 938ed77730..f4449e9ea1 100755 --- a/libraries/gpu/src/gpu/GLBackendPipeline.cpp +++ b/libraries/gpu/src/gpu/GLBackendPipeline.cpp @@ -63,11 +63,6 @@ void GLBackend::do_setPipeline(Batch& batch, uint32 paramOffset) { if (_pipeline._pipeline == pipeline) { return; } - - if (_pipeline._needStateSync) { - syncPipelineStateCache(); - _pipeline._needStateSync = false; - } // null pipeline == reset if (!pipeline) { @@ -76,6 +71,11 @@ void GLBackend::do_setPipeline(Batch& batch, uint32 paramOffset) { _pipeline._program = 0; _pipeline._invalidProgram = true; +#if (GPU_TRANSFORM_PROFILE == GPU_CORE) +#else + _pipeline._program_transformCamera_viewInverse = -1; +#endif + _pipeline._state = nullptr; _pipeline._invalidState = true; } else { @@ -88,6 +88,11 @@ void GLBackend::do_setPipeline(Batch& batch, uint32 paramOffset) { if (_pipeline._program != pipelineObject->_program->_program) { _pipeline._program = pipelineObject->_program->_program; _pipeline._invalidProgram = true; + +#if (GPU_TRANSFORM_PROFILE == GPU_CORE) +#else + _pipeline._program_transformCamera_viewInverse = pipelineObject->_program->_transformCamera_viewInverse; +#endif } // Now for the state @@ -108,17 +113,7 @@ void GLBackend::do_setPipeline(Batch& batch, uint32 paramOffset) { } } -#define DEBUG_GLSTATE void GLBackend::updatePipeline() { -#ifdef DEBUG_GLSTATE - if (_pipeline._needStateSync) { - State::Data state; - getCurrentGLState(state); - State::Signature signature = State::evalSignature(state); - (void) signature; // quiet compiler - } -#endif - if (_pipeline._invalidProgram) { // doing it here is aproblem for calls to glUniform.... so will do it on assing... glUseProgram(_pipeline._program); @@ -145,6 +140,14 @@ void GLBackend::updatePipeline() { } _pipeline._invalidState = false; } + +#if (GPU_TRANSFORM_PROFILE == GPU_CORE) +#else + // If shader program needs the inverseView we need to provide it + if (_pipeline._program_transformCamera_viewInverse >= 0) { + glUniformMatrix4fv(_pipeline._program_transformCamera_viewInverse, 1, false, (const GLfloat*) &_transform._transformCamera._viewInverse); + } +#endif } void GLBackend::do_setUniformBuffer(Batch& batch, uint32 paramOffset) { diff --git a/libraries/gpu/src/gpu/GLBackendShader.cpp b/libraries/gpu/src/gpu/GLBackendShader.cpp index 75b3df3e33..e0ea2f2d98 100755 --- a/libraries/gpu/src/gpu/GLBackendShader.cpp +++ b/libraries/gpu/src/gpu/GLBackendShader.cpp @@ -106,6 +106,11 @@ void makeBindings(GLBackend::GLShader* shader) { glUniformBlockBinding(glprogram, loc, gpu::TRANSFORM_CAMERA_SLOT); shader->_transformCameraSlot = gpu::TRANSFORM_CAMERA_SLOT; } +#else + loc = glGetUniformLocation(glprogram, "transformCamera_viewInverse"); + if (loc >= 0) { + shader->_transformCamera_viewInverse = loc; + } #endif } diff --git a/libraries/gpu/src/gpu/GLBackendTransform.cpp b/libraries/gpu/src/gpu/GLBackendTransform.cpp index d619d0afee..3f760e4cc8 100755 --- a/libraries/gpu/src/gpu/GLBackendTransform.cpp +++ b/libraries/gpu/src/gpu/GLBackendTransform.cpp @@ -55,6 +55,25 @@ void GLBackend::killTransform() { #else #endif } + +void GLBackend::syncTransformStateCache() { + _transform._invalidProj = true; + _transform._invalidView = true; + _transform._invalidModel = true; + + GLint currentMode; + glGetIntegerv(GL_MATRIX_MODE, ¤tMode); + _transform._lastMode = currentMode; + + glGetFloatv(GL_PROJECTION_MATRIX, (float*) &_transform._projection); + + Mat4 modelView; + glGetFloatv(GL_MODELVIEW_MATRIX, (float*) &modelView); + auto modelViewInv = glm::inverse(modelView); + _transform._view.evalFromRawMatrix(modelViewInv); + _transform._model.setIdentity(); +} + void GLBackend::updateTransform() { // Check all the dirty flags and update the state accordingly if (_transform._invalidProj) { diff --git a/libraries/gpu/src/gpu/Resource.cpp b/libraries/gpu/src/gpu/Resource.cpp index 7d2757e15c..046cf9fe40 100644 --- a/libraries/gpu/src/gpu/Resource.cpp +++ b/libraries/gpu/src/gpu/Resource.cpp @@ -8,8 +8,6 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // - -#include "Context.h" #include "Resource.h" #include diff --git a/libraries/gpu/src/gpu/Stream.cpp b/libraries/gpu/src/gpu/Stream.cpp index e23a730370..634545b4dd 100644 --- a/libraries/gpu/src/gpu/Stream.cpp +++ b/libraries/gpu/src/gpu/Stream.cpp @@ -10,8 +10,8 @@ // #include "Stream.h" - -#include //min max and more + +#include //min max and more using namespace gpu; diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 0d9664f1ab..d5c3635816 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -17,11 +17,11 @@ namespace gpu { -// THe spherical harmonics is a nice tool for cubemap, so if required, the irradiance SH can be automatically generated -// with the cube texture -class Texture; -class SphericalHarmonics { -public: +// THe spherical harmonics is a nice tool for cubemap, so if required, the irradiance SH can be automatically generated +// with the cube texture +class Texture; +class SphericalHarmonics { +public: glm::vec3 L00 ; float spare0; glm::vec3 L1m1 ; float spare1; glm::vec3 L10 ; float spare2; @@ -44,15 +44,15 @@ public: VINE_STREET_KITCHEN, BREEZEWAY, CAMPUS_SUNSET, - FUNSTON_BEACH_SUNSET, - - NUM_PRESET, + FUNSTON_BEACH_SUNSET, + + NUM_PRESET, }; void assignPreset(int p); void evalFromTexture(const Texture& texture); -}; +}; typedef std::shared_ptr< SphericalHarmonics > SHPointer; class Sampler { @@ -437,8 +437,10 @@ public: explicit operator bool() const { return bool(_texture); } bool operator !() const { return (!_texture); } + + bool isValid() const { return bool(_texture); } }; -typedef std::vector TextureViews; +typedef std::vector TextureViews; }; diff --git a/libraries/gpu/src/gpu/Transform.slh b/libraries/gpu/src/gpu/Transform.slh index 57a367fdba..d01fe128ae 100644 --- a/libraries/gpu/src/gpu/Transform.slh +++ b/libraries/gpu/src/gpu/Transform.slh @@ -86,6 +86,8 @@ TransformCamera getTransformCamera() { return camera; } +uniform mat4 transformCamera_viewInverse; + <@endif@> <@endfunc@> @@ -103,6 +105,23 @@ TransformCamera getTransformCamera() { <@endif@> <@endfunc@> +<@func $transformModelToEyeAndClipPos(cameraTransform, objectTransform, modelPos, eyePos, clipPos)@> +<@if GPU_TRANSFORM_PROFILE == GPU_CORE@> + + { // transformModelToClipPos + vec4 _worldpos = (<$objectTransform$>._model * <$modelPos$>); + <$eyePos$> = (<$cameraTransform$>._view * _worldpos); + vec4 _eyepos =(<$objectTransform$>._model * <$modelPos$>) + vec4(-<$modelPos$>.w * <$cameraTransform$>._viewInverse[3].xyz, 0.0); + <$clipPos$> = <$cameraTransform$>._projectionViewUntranslated * _eyepos; + // <$eyePos$> = (<$cameraTransform$>._projectionInverse * <$clipPos$>); + } +<@else@> + <$eyePos$> = gl_ModelViewMatrix * <$modelPos$>; + <$clipPos$> = gl_ModelViewProjectionMatrix * <$modelPos$>; +<@endif@> +<@endfunc@> + <@func transformModelToEyeDir(cameraTransform, objectTransform, modelDir, eyeDir)@> <@if GPU_TRANSFORM_PROFILE == GPU_CORE@> { // transformModelToEyeDir @@ -127,7 +146,7 @@ TransformCamera getTransformCamera() { <$worldDir$> = vec3(<$cameraTransform$>._viewInverse * vec4(<$eyeDir$>.xyz, 0.0)); } <@else@> - <$worldDir$> = vec3(gl_ModelViewMatrixInverse * vec4(<$eyeDir$>.xyz, 0.0)); + <$worldDir$> = vec3(transformCamera_viewInverse * vec4(<$eyeDir$>.xyz, 0.0)); <@endif@> <@endfunc@> diff --git a/libraries/model/src/model/Material.cpp b/libraries/model/src/model/Material.cpp index 55572a5122..a3448889b0 100755 --- a/libraries/model/src/model/Material.cpp +++ b/libraries/model/src/model/Material.cpp @@ -14,7 +14,7 @@ using namespace model; using namespace gpu; Material::Material() : - _flags(0), + _key(0), _schemaBuffer(), _textureMap() { @@ -26,13 +26,13 @@ Material::Material() : } Material::Material(const Material& material) : - _flags(material._flags), + _key(material._key), _schemaBuffer(material._schemaBuffer), _textureMap(material._textureMap) { } Material& Material::operator= (const Material& material) { - _flags = (material._flags); + _key = (material._key); _schemaBuffer = (material._schemaBuffer); _textureMap = (material._textureMap); @@ -43,52 +43,32 @@ Material::~Material() { } void Material::setDiffuse(const Color& diffuse) { - if (glm::any(glm::greaterThan(diffuse, Color(0.0f)))) { - _flags.set(DIFFUSE_BIT); - } else { - _flags.reset(DIFFUSE_BIT); - } + _key.setDiffuse(glm::any(glm::greaterThan(diffuse, Color(0.0f)))); _schemaBuffer.edit()._diffuse = diffuse; } -void Material::setSpecular(const Color& specular) { - if (glm::any(glm::greaterThan(specular, Color(0.0f)))) { - _flags.set(SPECULAR_BIT); - } else { - _flags.reset(SPECULAR_BIT); - } - _schemaBuffer.edit()._specular = specular; +void Material::setMetallic(float metallic) { + _key.setMetallic(metallic > 0.0f); + _schemaBuffer.edit()._metallic = glm::vec3(metallic); } void Material::setEmissive(const Color& emissive) { - if (glm::any(glm::greaterThan(emissive, Color(0.0f)))) { - _flags.set(EMISSIVE_BIT); - } else { - _flags.reset(EMISSIVE_BIT); - } + _key.setEmissive(glm::any(glm::greaterThan(emissive, Color(0.0f)))); _schemaBuffer.edit()._emissive = emissive; } -void Material::setShininess(float shininess) { - if (shininess > 0.0f) { - _flags.set(SHININESS_BIT); - } else { - _flags.reset(SHININESS_BIT); - } - _schemaBuffer.edit()._shininess = shininess; +void Material::setGloss(float gloss) { + _key.setGloss((gloss > 0.0f)); + _schemaBuffer.edit()._gloss = gloss; } void Material::setOpacity(float opacity) { - if (opacity >= 1.0f) { - _flags.reset(TRANSPARENT_BIT); - } else { - _flags.set(TRANSPARENT_BIT); - } + _key.setTransparent((opacity < 1.0f)); _schemaBuffer.edit()._opacity = opacity; } void Material::setTextureView(MapChannel channel, const gpu::TextureView& view) { - _flags.set(DIFFUSE_MAP_BIT + channel); + _key.setMapChannel(channel, (view.isValid())); _textureMap[channel] = view; } diff --git a/libraries/model/src/model/Material.h b/libraries/model/src/model/Material.h index ea0ab808e9..392fd918a1 100755 --- a/libraries/model/src/model/Material.h +++ b/libraries/model/src/model/Material.h @@ -23,6 +23,177 @@ namespace model { +// Material Key is a coarse trait description of a material used to classify the materials +class MaterialKey { +public: + enum FlagBit { + EMISSIVE_VAL_BIT = 0, + DIFFUSE_VAL_BIT, + METALLIC_VAL_BIT, + GLOSS_VAL_BIT, + TRANSPARENT_VAL_BIT, + + EMISSIVE_MAP_BIT, + DIFFUSE_MAP_BIT, + METALLIC_MAP_BIT, + GLOSS_MAP_BIT, + TRANSPARENT_MAP_BIT, + NORMAL_MAP_BIT, + + NUM_FLAGS, + }; + typedef std::bitset Flags; + + enum MapChannel { + EMISSIVE_MAP = 0, + DIFFUSE_MAP, + METALLIC_MAP, + GLOSS_MAP, + TRANSPARENT_MAP, + NORMAL_MAP, + + NUM_MAP_CHANNELS, + }; + + // The signature is the Flags + Flags _flags; + + MaterialKey() : _flags(0) {} + MaterialKey(const Flags& flags) : _flags(flags) {} + + class Builder { + Flags _flags{ 0 }; + public: + Builder() {} + + MaterialKey build() const { return MaterialKey(_flags); } + + Builder& withEmissive() { _flags.set(EMISSIVE_VAL_BIT); return (*this); } + Builder& withDiffuse() { _flags.set(DIFFUSE_VAL_BIT); return (*this); } + Builder& withMetallic() { _flags.set(METALLIC_VAL_BIT); return (*this); } + Builder& withGloss() { _flags.set(GLOSS_VAL_BIT); return (*this); } + Builder& withTransparent() { _flags.set(TRANSPARENT_VAL_BIT); return (*this); } + + Builder& withEmissiveMap() { _flags.set(EMISSIVE_MAP_BIT); return (*this); } + Builder& withDiffuseMap() { _flags.set(DIFFUSE_MAP_BIT); return (*this); } + Builder& withMetallicMap() { _flags.set(METALLIC_MAP_BIT); return (*this); } + Builder& withGlossMap() { _flags.set(GLOSS_MAP_BIT); return (*this); } + Builder& withTransparentMap() { _flags.set(TRANSPARENT_MAP_BIT); return (*this); } + + Builder& withNormalMap() { _flags.set(NORMAL_MAP_BIT); return (*this); } + + // Convenient standard keys that we will keep on using all over the place + static MaterialKey opaqueDiffuse() { return Builder().withDiffuse().build(); } + }; + + void setEmissive(bool value) { _flags.set(EMISSIVE_VAL_BIT, value); } + bool isEmissive() const { return _flags[EMISSIVE_VAL_BIT]; } + + void setEmissiveMap(bool value) { _flags.set(EMISSIVE_MAP_BIT, value); } + bool isEmissiveMap() const { return _flags[EMISSIVE_MAP_BIT]; } + + void setDiffuse(bool value) { _flags.set(DIFFUSE_VAL_BIT, value); } + bool isDiffuse() const { return _flags[DIFFUSE_VAL_BIT]; } + + void setDiffuseMap(bool value) { _flags.set(DIFFUSE_MAP_BIT, value); } + bool isDiffuseMap() const { return _flags[DIFFUSE_MAP_BIT]; } + + void setMetallic(bool value) { _flags.set(METALLIC_VAL_BIT, value); } + bool isMetallic() const { return _flags[METALLIC_VAL_BIT]; } + + void setMetallicMap(bool value) { _flags.set(METALLIC_MAP_BIT, value); } + bool isMetallicMap() const { return _flags[METALLIC_MAP_BIT]; } + + void setGloss(bool value) { _flags.set(GLOSS_VAL_BIT, value); } + bool isGloss() const { return _flags[GLOSS_VAL_BIT]; } + + void setGlossMap(bool value) { _flags.set(GLOSS_MAP_BIT, value); } + bool isGlossMap() const { return _flags[GLOSS_MAP_BIT]; } + + void setTransparent(bool value) { _flags.set(TRANSPARENT_VAL_BIT, value); } + bool isTransparent() const { return _flags[TRANSPARENT_VAL_BIT]; } + bool isOpaque() const { return !_flags[TRANSPARENT_VAL_BIT]; } + + void setTransparentMap(bool value) { _flags.set(TRANSPARENT_MAP_BIT, value); } + bool isTransparentMap() const { return _flags[TRANSPARENT_MAP_BIT]; } + + void setNormalMap(bool value) { _flags.set(NORMAL_MAP_BIT, value); } + bool isNormalMap() const { return _flags[NORMAL_MAP_BIT]; } + + void setMapChannel(MapChannel channel, bool value) { _flags.set(EMISSIVE_MAP_BIT + channel, value); } + bool isMapChannel(MapChannel channel) const { return _flags[EMISSIVE_MAP_BIT + channel]; } + +}; + + +class MaterialFilter { +public: + MaterialKey::Flags _value{ 0 }; + MaterialKey::Flags _mask{ 0 }; + + + MaterialFilter(const MaterialKey::Flags& value = MaterialKey::Flags(0), const MaterialKey::Flags& mask = MaterialKey::Flags(0)) : _value(value), _mask(mask) {} + + class Builder { + MaterialKey::Flags _value{ 0 }; + MaterialKey::Flags _mask{ 0 }; + public: + Builder() {} + + MaterialFilter build() const { return MaterialFilter(_value, _mask); } + + Builder& withoutEmissive() { _value.reset(MaterialKey::EMISSIVE_VAL_BIT); _mask.set(MaterialKey::EMISSIVE_VAL_BIT); return (*this); } + Builder& withEmissive() { _value.set(MaterialKey::EMISSIVE_VAL_BIT); _mask.set(MaterialKey::EMISSIVE_VAL_BIT); return (*this); } + + Builder& withoutEmissiveMap() { _value.reset(MaterialKey::EMISSIVE_MAP_BIT); _mask.set(MaterialKey::EMISSIVE_MAP_BIT); return (*this); } + Builder& withEmissiveMap() { _value.set(MaterialKey::EMISSIVE_MAP_BIT); _mask.set(MaterialKey::EMISSIVE_MAP_BIT); return (*this); } + + Builder& withoutDiffuse() { _value.reset(MaterialKey::DIFFUSE_VAL_BIT); _mask.set(MaterialKey::DIFFUSE_VAL_BIT); return (*this); } + Builder& withDiffuse() { _value.set(MaterialKey::DIFFUSE_VAL_BIT); _mask.set(MaterialKey::DIFFUSE_VAL_BIT); return (*this); } + + Builder& withoutDiffuseMap() { _value.reset(MaterialKey::DIFFUSE_MAP_BIT); _mask.set(MaterialKey::DIFFUSE_MAP_BIT); return (*this); } + Builder& withDiffuseMap() { _value.set(MaterialKey::DIFFUSE_MAP_BIT); _mask.set(MaterialKey::DIFFUSE_MAP_BIT); return (*this); } + + Builder& withoutMetallic() { _value.reset(MaterialKey::METALLIC_VAL_BIT); _mask.set(MaterialKey::METALLIC_VAL_BIT); return (*this); } + Builder& withMetallic() { _value.set(MaterialKey::METALLIC_VAL_BIT); _mask.set(MaterialKey::METALLIC_VAL_BIT); return (*this); } + + Builder& withoutMetallicMap() { _value.reset(MaterialKey::METALLIC_MAP_BIT); _mask.set(MaterialKey::METALLIC_MAP_BIT); return (*this); } + Builder& withMetallicMap() { _value.set(MaterialKey::METALLIC_MAP_BIT); _mask.set(MaterialKey::METALLIC_MAP_BIT); return (*this); } + + Builder& withoutGloss() { _value.reset(MaterialKey::GLOSS_VAL_BIT); _mask.set(MaterialKey::GLOSS_VAL_BIT); return (*this); } + Builder& withGloss() { _value.set(MaterialKey::GLOSS_VAL_BIT); _mask.set(MaterialKey::GLOSS_VAL_BIT); return (*this); } + + Builder& withoutGlossMap() { _value.reset(MaterialKey::GLOSS_MAP_BIT); _mask.set(MaterialKey::GLOSS_MAP_BIT); return (*this); } + Builder& withGlossMap() { _value.set(MaterialKey::GLOSS_MAP_BIT); _mask.set(MaterialKey::GLOSS_MAP_BIT); return (*this); } + + Builder& withoutTransparent() { _value.reset(MaterialKey::TRANSPARENT_VAL_BIT); _mask.set(MaterialKey::TRANSPARENT_VAL_BIT); return (*this); } + Builder& withTransparent() { _value.set(MaterialKey::TRANSPARENT_VAL_BIT); _mask.set(MaterialKey::TRANSPARENT_VAL_BIT); return (*this); } + + Builder& withoutTransparentMap() { _value.reset(MaterialKey::TRANSPARENT_MAP_BIT); _mask.set(MaterialKey::TRANSPARENT_MAP_BIT); return (*this); } + Builder& withTransparentMap() { _value.set(MaterialKey::TRANSPARENT_MAP_BIT); _mask.set(MaterialKey::TRANSPARENT_MAP_BIT); return (*this); } + + Builder& withoutNormalMap() { _value.reset(MaterialKey::NORMAL_MAP_BIT); _mask.set(MaterialKey::NORMAL_MAP_BIT); return (*this); } + Builder& withNormalMap() { _value.set(MaterialKey::NORMAL_MAP_BIT); _mask.set(MaterialKey::NORMAL_MAP_BIT); return (*this); } + + // Convenient standard keys that we will keep on using all over the place + static MaterialFilter opaqueDiffuse() { return Builder().withDiffuse().withoutTransparent().build(); } + }; + + // Item Filter operator testing if a key pass the filter + bool test(const MaterialKey& key) const { return (key._flags & _mask) == (_value & _mask); } + + class Less { + public: + bool operator() (const MaterialFilter& left, const MaterialFilter& right) const { + if (left._value.to_ulong() == right._value.to_ulong()) { + return left._mask.to_ulong() < right._mask.to_ulong(); + } else { + return left._value.to_ulong() < right._value.to_ulong(); + } + } + }; +}; + class Material { public: typedef gpu::BufferView UniformBufferView; @@ -30,52 +201,27 @@ public: typedef glm::vec3 Color; - enum MapChannel { - DIFFUSE_MAP = 0, - SPECULAR_MAP, - SHININESS_MAP, - EMISSIVE_MAP, - OPACITY_MAP, - NORMAL_MAP, - - NUM_MAPS, - }; + typedef MaterialKey::MapChannel MapChannel; typedef std::map TextureMap; - typedef std::bitset MapFlags; - - enum FlagBit { - DIFFUSE_BIT = 0, - SPECULAR_BIT, - SHININESS_BIT, - EMISSIVE_BIT, - TRANSPARENT_BIT, - - DIFFUSE_MAP_BIT, - SPECULAR_MAP_BIT, - SHININESS_MAP_BIT, - EMISSIVE_MAP_BIT, - OPACITY_MAP_BIT, - NORMAL_MAP_BIT, - - NUM_FLAGS, - }; - typedef std::bitset Flags; + typedef std::bitset MapFlags; Material(); Material(const Material& material); Material& operator= (const Material& material); virtual ~Material(); + const MaterialKey& getKey() const { return _key; } + const Color& getEmissive() const { return _schemaBuffer.get()._emissive; } const Color& getDiffuse() const { return _schemaBuffer.get()._diffuse; } - const Color& getSpecular() const { return _schemaBuffer.get()._specular; } - float getShininess() const { return _schemaBuffer.get()._shininess; } + float getMetallic() const { return _schemaBuffer.get()._metallic.x; } + float getGloss() const { return _schemaBuffer.get()._gloss; } float getOpacity() const { return _schemaBuffer.get()._opacity; } - void setDiffuse(const Color& diffuse); - void setSpecular(const Color& specular); void setEmissive(const Color& emissive); - void setShininess(float shininess); + void setDiffuse(const Color& diffuse); + void setMetallic(float metallic); + void setGloss(float gloss); void setOpacity(float opacity); // Schema to access the attribute values of the material @@ -84,8 +230,8 @@ public: Color _diffuse{0.5f}; float _opacity{1.f}; - Color _specular{0.03f}; - float _shininess{0.1f}; + Color _metallic{0.03f}; + float _gloss{0.1f}; Color _emissive{0.0f}; float _spare0{0.0f}; glm::vec4 _spareVec4{0.0f}; // for alignment beauty, Material size == Mat4x4 @@ -100,7 +246,7 @@ public: protected: - Flags _flags; + MaterialKey _key; UniformBufferView _schemaBuffer; TextureMap _textureMap; diff --git a/libraries/model/src/model/Skybox.cpp b/libraries/model/src/model/Skybox.cpp index a34a3be3fd..933c737b83 100755 --- a/libraries/model/src/model/Skybox.cpp +++ b/libraries/model/src/model/Skybox.cpp @@ -44,70 +44,73 @@ void Skybox::setCubemap(const gpu::TexturePointer& cubemap) { void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Skybox& skybox) { - if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) { + if (skybox.getCubemap()) { + if (skybox.getCubemap()->isDefined()) { - static gpu::PipelinePointer thePipeline; - static gpu::BufferPointer theBuffer; - static gpu::Stream::FormatPointer theFormat; - static gpu::BufferPointer theConstants; - int SKYBOX_CONSTANTS_SLOT = 0; // need to be defined by the compilation of the shader - if (!thePipeline) { - auto skyVS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(Skybox_vert))); - auto skyFS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(Skybox_frag))); - auto skyShader = gpu::ShaderPointer(gpu::Shader::createProgram(skyVS, skyFS)); + static gpu::PipelinePointer thePipeline; + static gpu::BufferPointer theBuffer; + static gpu::Stream::FormatPointer theFormat; + static gpu::BufferPointer theConstants; + int SKYBOX_CONSTANTS_SLOT = 0; // need to be defined by the compilation of the shader + if (!thePipeline) { + auto skyVS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(Skybox_vert))); + auto skyFS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(Skybox_frag))); + auto skyShader = gpu::ShaderPointer(gpu::Shader::createProgram(skyVS, skyFS)); - gpu::Shader::BindingSet bindings; - bindings.insert(gpu::Shader::Binding(std::string("cubeMap"), 0)); - if (!gpu::Shader::makeProgram(*skyShader, bindings)) { + gpu::Shader::BindingSet bindings; + bindings.insert(gpu::Shader::Binding(std::string("cubeMap"), 0)); + if (!gpu::Shader::makeProgram(*skyShader, bindings)) { - } + } - SKYBOX_CONSTANTS_SLOT = skyShader->getBuffers().findLocation("skyboxBuffer"); - if (SKYBOX_CONSTANTS_SLOT == gpu::Shader::INVALID_LOCATION) { - SKYBOX_CONSTANTS_SLOT = skyShader->getUniforms().findLocation("skyboxBuffer"); - } + SKYBOX_CONSTANTS_SLOT = skyShader->getBuffers().findLocation("skyboxBuffer"); + if (SKYBOX_CONSTANTS_SLOT == gpu::Shader::INVALID_LOCATION) { + SKYBOX_CONSTANTS_SLOT = skyShader->getUniforms().findLocation("skyboxBuffer"); + } - auto skyState = gpu::StatePointer(new gpu::State()); + auto skyState = gpu::StatePointer(new gpu::State()); - thePipeline = gpu::PipelinePointer(gpu::Pipeline::create(skyShader, skyState)); + thePipeline = gpu::PipelinePointer(gpu::Pipeline::create(skyShader, skyState)); - const float CLIP = 1.0; - const glm::vec2 vertices[4] = { {-CLIP, -CLIP}, {CLIP, -CLIP}, {-CLIP, CLIP}, {CLIP, CLIP}}; - theBuffer.reset(new gpu::Buffer(sizeof(vertices), (const gpu::Byte*) vertices)); + const float CLIP = 1.0; + const glm::vec2 vertices[4] = { {-CLIP, -CLIP}, {CLIP, -CLIP}, {-CLIP, CLIP}, {CLIP, CLIP}}; + theBuffer.reset(new gpu::Buffer(sizeof(vertices), (const gpu::Byte*) vertices)); - theFormat.reset(new gpu::Stream::Format()); - theFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); + theFormat.reset(new gpu::Stream::Format()); + theFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); - auto color = glm::vec4(1.0f); - theConstants.reset(new gpu::Buffer(sizeof(color), (const gpu::Byte*) &color)); + auto color = glm::vec4(1.0f); + theConstants.reset(new gpu::Buffer(sizeof(color), (const gpu::Byte*) &color)); + } + + glm::mat4 projMat; + viewFrustum.evalProjectionMatrix(projMat); + + Transform viewTransform; + viewFrustum.evalViewTransform(viewTransform); + + if (glm::all(glm::equal(skybox.getColor(), glm::vec3(0.0f)))) { + auto color = glm::vec4(1.0f); + theConstants->setSubData(0, sizeof(color), (const gpu::Byte*) &color); + } else { + theConstants->setSubData(0, sizeof(Color), (const gpu::Byte*) &skybox.getColor()); + } + + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewTransform); + batch.setModelTransform(Transform()); // only for Mac + batch.setPipeline(thePipeline); + batch.setInputBuffer(gpu::Stream::POSITION, theBuffer, 0, 8); + batch.setUniformBuffer(SKYBOX_CONSTANTS_SLOT, theConstants, 0, theConstants->getSize()); + batch.setInputFormat(theFormat); + batch.setUniformTexture(0, skybox.getCubemap()); + batch.draw(gpu::TRIANGLE_STRIP, 4); } - glm::mat4 projMat; - viewFrustum.evalProjectionMatrix(projMat); - - Transform viewTransform; - viewFrustum.evalViewTransform(viewTransform); - - if (glm::all(glm::equal(skybox.getColor(), glm::vec3(0.0f)))) { - auto color = glm::vec4(1.0f); - theConstants->setSubData(0, sizeof(color), (const gpu::Byte*) &color); - } else { - theConstants->setSubData(0, sizeof(Color), (const gpu::Byte*) &skybox.getColor()); - } - - batch.setProjectionTransform(projMat); - batch.setViewTransform(viewTransform); - batch.setModelTransform(Transform()); // only for Mac - batch.setPipeline(thePipeline); - batch.setInputBuffer(gpu::Stream::POSITION, theBuffer, 0, 8); - batch.setUniformBuffer(SKYBOX_CONSTANTS_SLOT, theConstants, 0, theConstants->getSize()); - batch.setInputFormat(theFormat); - batch.setUniformTexture(0, skybox.getCubemap()); - batch.draw(gpu::TRIANGLE_STRIP, 4); } else { // skybox has no cubemap, just clear the color buffer auto color = skybox.getColor(); - batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(color, 1.0f), 0.f, 0); + batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(color, 0.0f), 0.f, 0); } } diff --git a/libraries/model/src/model/TextureStorage.h b/libraries/model/src/model/TextureStorage.h index ebc027298b..a6752d21b2 100755 --- a/libraries/model/src/model/TextureStorage.h +++ b/libraries/model/src/model/TextureStorage.h @@ -24,7 +24,7 @@ typedef glm::vec3 Color; class TextureUsage { public: gpu::Texture::Type _type{ gpu::Texture::TEX_2D }; - Material::MapFlags _materialUsage{ Material::DIFFUSE_MAP }; + Material::MapFlags _materialUsage{ MaterialKey::DIFFUSE_MAP }; int _environmentUsage = 0; }; diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 3c6b7bd3f5..642ca4748d 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -30,11 +30,12 @@ const QString SETTINGS_CURRENT_ADDRESS_KEY = "address"; Setting::Handle currentAddressHandle(QStringList() << ADDRESS_MANAGER_SETTINGS_GROUP << "address", DEFAULT_HIFI_ADDRESS); AddressManager::AddressManager() : - _rootPlaceName(), + _host(), _rootPlaceID(), _positionGetter(NULL), _orientationGetter(NULL) { + } bool AddressManager::isConnected() { @@ -45,7 +46,7 @@ const QUrl AddressManager::currentAddress() const { QUrl hifiURL; hifiURL.setScheme(HIFI_URL_SCHEME); - hifiURL.setHost(_rootPlaceName); + hifiURL.setHost(_host); hifiURL.setPath(currentPath()); return hifiURL; @@ -123,6 +124,10 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) { + (lookupUrl.port() == -1 ? "" : ":" + QString::number(lookupUrl.port())))) { // we may have a path that defines a relative viewpoint - if so we should jump to that now handlePath(lookupUrl.path()); + } else if (handleDomainID(lookupUrl.host())){ + // no place name - this is probably a domain ID + // try to look up the domain ID on the metaverse API + attemptDomainIDLookup(lookupUrl.host(), lookupUrl.path()); } else { // wasn't an address - lookup the place name // we may have a path that defines a relative viewpoint - pass that through the lookup so we can go to it after @@ -161,11 +166,18 @@ void AddressManager::handleLookupString(const QString& lookupString) { } } +const QString DATA_OBJECT_DOMAIN_KEY = "domain"; + + void AddressManager::handleAPIResponse(QNetworkReply& requestReply) { QJsonObject responseObject = QJsonDocument::fromJson(requestReply.readAll()).object(); QJsonObject dataObject = responseObject["data"].toObject(); - goToAddressFromObject(dataObject.toVariantMap(), requestReply); + if (!dataObject.isEmpty()) { + goToAddressFromObject(dataObject.toVariantMap(), requestReply); + } else if (responseObject.contains(DATA_OBJECT_DOMAIN_KEY)) { + goToAddressFromObject(responseObject.toVariantMap(), requestReply); + } emit lookupResultsFinished(); } @@ -180,6 +192,8 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const QVariantMap locationMap; if (dataObject.contains(DATA_OBJECT_PLACE_KEY)) { locationMap = dataObject[DATA_OBJECT_PLACE_KEY].toMap(); + } else if (dataObject.contains(DATA_OBJECT_DOMAIN_KEY)) { + locationMap = dataObject; } else { locationMap = dataObject[DATA_OBJECT_USER_LOCATION_KEY].toMap(); } @@ -204,7 +218,11 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const const QString DOMAIN_NETWORK_PORT_KEY = "network_port"; const QString DOMAIN_ICE_SERVER_ADDRESS_KEY = "ice_server_address"; - DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::HandleAddress); + DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::HandleAddress); + + const QString DOMAIN_ID_KEY = "id"; + QString domainIDString = domainObject[DOMAIN_ID_KEY].toString(); + QUuid domainID(domainIDString); if (domainObject.contains(DOMAIN_NETWORK_ADDRESS_KEY)) { QString domainHostname = domainObject[DOMAIN_NETWORK_ADDRESS_KEY].toString(); @@ -219,10 +237,6 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const } else { QString iceServerAddress = domainObject[DOMAIN_ICE_SERVER_ADDRESS_KEY].toString(); - const QString DOMAIN_ID_KEY = "id"; - QString domainIDString = domainObject[DOMAIN_ID_KEY].toString(); - QUuid domainID(domainIDString); - qCDebug(networking) << "Possible domain change required to connect to domain with ID" << domainID << "via ice-server at" << iceServerAddress; @@ -235,8 +249,12 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const // set our current root place name to the name that came back const QString PLACE_NAME_KEY = "name"; - QString newRootPlaceName = rootMap[PLACE_NAME_KEY].toString(); - setRootPlaceName(newRootPlaceName); + QString placeName = rootMap[PLACE_NAME_KEY].toString(); + if (!placeName.isEmpty()) { + setHost(placeName); + } else { + setHost(domainIDString); + } // check if we had a path to override the path returned QString overridePath = reply.property(OVERRIDE_PATH_KEY).toString(); @@ -304,6 +322,24 @@ void AddressManager::attemptPlaceNameLookup(const QString& lookupString, const Q QByteArray(), NULL, requestParams); } +const QString GET_DOMAIN_ID = "/api/v1/domains/%1"; + +void AddressManager::attemptDomainIDLookup(const QString& lookupString, const QString& overridePath) { + // assume this is a domain ID and see if we can get any info on it + QString domainID = QUrl::toPercentEncoding(lookupString); + + QVariantMap requestParams; + if (!overridePath.isEmpty()) { + requestParams.insert(OVERRIDE_PATH_KEY, overridePath); + } + + AccountManager::getInstance().sendRequest(GET_DOMAIN_ID.arg(domainID), + AccountManagerAuth::None, + QNetworkAccessManager::GetOperation, + apiCallbackParameters(), + QByteArray(), NULL, requestParams); +} + bool AddressManager::handleNetworkAddress(const QString& lookupString) { const QString IP_ADDRESS_REGEX_STRING = "^((?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}" "(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))(?::(\\d{1,5}))?$"; @@ -335,7 +371,7 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString) { quint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT; if (!hostnameRegex.cap(2).isEmpty()) { - domainPort = (qint16) hostnameRegex.cap(2).toInt(); + domainPort = (qint16)hostnameRegex.cap(2).toInt(); } emit lookupResultsFinished(); @@ -347,6 +383,14 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString) { return false; } +bool AddressManager::handleDomainID(const QString& host) { + const QString UUID_REGEX_STRING = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"; + + QRegExp domainIDRegex(UUID_REGEX_STRING, Qt::CaseInsensitive); + + return (domainIDRegex.indexIn(host) != -1); +} + void AddressManager::handlePath(const QString& path) { if (!handleViewpoint(path)) { qCDebug(networking) << "User entered path could not be handled as a viewpoint - " << path << @@ -372,6 +416,9 @@ bool AddressManager::handleViewpoint(const QString& viewpointString, bool should positionRegex.cap(2).toFloat(), positionRegex.cap(3).toFloat()); + // we're about to jump positions - store the current address in our history + addCurrentAddressToHistory(); + if (!isNaN(newPosition.x) && !isNaN(newPosition.y) && !isNaN(newPosition.z)) { glm::quat newOrientation; @@ -422,16 +469,21 @@ bool AddressManager::handleUsername(const QString& lookupString) { return false; } -void AddressManager::setRootPlaceName(const QString& rootPlaceName) { - if (rootPlaceName != _rootPlaceName) { - _rootPlaceName = rootPlaceName; - emit rootPlaceNameChanged(_rootPlaceName); +void AddressManager::setHost(const QString& host) { + if (host != _host) { + + // if the host is being changed we should store current address in the history + addCurrentAddressToHistory(); + + _host = host; + emit hostChanged(_host); } } void AddressManager::setDomainInfo(const QString& hostname, quint16 port) { - _rootPlaceName = hostname; + setHost(hostname); + _rootPlaceID = QUuid(); qCDebug(networking) << "Possible domain change required to connect to domain at" << hostname << "on" << port; @@ -457,3 +509,22 @@ void AddressManager::copyAddress() { void AddressManager::copyPath() { QApplication::clipboard()->setText(currentPath()); } + +void AddressManager::addCurrentAddressToHistory() { + if (_lastHistoryAppend == 0) { + // we don't store the first address on application load + // just update the last append time so the next is stored + _lastHistoryAppend = usecTimestampNow(); + } else { + const quint64 DOUBLE_STORE_THRESHOLD_USECS = 500000; + + // avoid double storing when the host changes and the viewpoint changes immediately after + if (usecTimestampNow() - _lastHistoryAppend > DOUBLE_STORE_THRESHOLD_USECS) { + // add the current address to the history + _history.append(currentAddress()); + + // change our last history append to now + _lastHistoryAppend = usecTimestampNow(); + } + } +} diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index 5831d62603..d950ae0275 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -35,7 +35,7 @@ class AddressManager : public QObject, public Dependency { Q_PROPERTY(bool isConnected READ isConnected) Q_PROPERTY(QUrl href READ currentAddress) Q_PROPERTY(QString protocol READ getProtocol) - Q_PROPERTY(QString hostname READ getRootPlaceName) + Q_PROPERTY(QString hostname READ getHost) Q_PROPERTY(QString pathname READ currentPath) public: bool isConnected(); @@ -46,10 +46,11 @@ public: const QUuid& getRootPlaceID() const { return _rootPlaceID; } - const QString& getRootPlaceName() const { return _rootPlaceName; } - void setRootPlaceName(const QString& rootPlaceName); + const QString& getHost() const { return _host; } + void setHost(const QString& host); void attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath = QString()); + void attemptDomainIDLookup(const QString& lookupString, const QString& overridePath = QString()); void setPositionGetter(PositionGetter positionGetter) { _positionGetter = positionGetter; } void setOrientationGetter(OrientationGetter orientationGetter) { _orientationGetter = orientationGetter; } @@ -78,7 +79,7 @@ signals: bool hasOrientationChange, const glm::quat& newOrientation, bool shouldFaceLocation); void pathChangeRequired(const QString& newPath); - void rootPlaceNameChanged(const QString& newRootPlaceName); + void hostChanged(const QString& newHost); protected: AddressManager(); private slots: @@ -95,11 +96,17 @@ private: void handlePath(const QString& path); bool handleViewpoint(const QString& viewpointString, bool shouldFace = false); bool handleUsername(const QString& lookupString); + bool handleDomainID(const QString& host); - QString _rootPlaceName; + void addCurrentAddressToHistory(); + + QString _host; QUuid _rootPlaceID; PositionGetter _positionGetter; OrientationGetter _orientationGetter; + + QList _history; + quint64 _lastHistoryAppend = 0; }; #endif // hifi_AddressManager_h diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index aa0ab262d7..123e140913 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -605,9 +605,6 @@ const int NUM_BYTES_STUN_HEADER = 20; void LimitedNodeList::sendSTUNRequest() { - static quint64 lastTimeStamp = usecTimestampNow(); - lastTimeStamp = usecTimestampNow(); - const int NUM_INITIAL_STUN_REQUESTS_BEFORE_FAIL = 10; if (!_hasCompletedInitialSTUN) { diff --git a/libraries/octree/src/OctreeHeadlessViewer.h b/libraries/octree/src/OctreeHeadlessViewer.h index 2adb33d3bf..70ee43be35 100644 --- a/libraries/octree/src/OctreeHeadlessViewer.h +++ b/libraries/octree/src/OctreeHeadlessViewer.h @@ -33,9 +33,7 @@ public: virtual void renderElement(OctreeElement* element, RenderArgs* args) { /* swallow these */ } 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) { /* swallow these */ } + virtual void render(RenderArgs* renderArgs) override { /* swallow these */ } void setJurisdictionListener(JurisdictionListener* jurisdictionListener) { _jurisdictionListener = jurisdictionListener; } diff --git a/libraries/octree/src/OctreeRenderer.cpp b/libraries/octree/src/OctreeRenderer.cpp index 946ad3aa1b..7852f1d4b4 100644 --- a/libraries/octree/src/OctreeRenderer.cpp +++ b/libraries/octree/src/OctreeRenderer.cpp @@ -164,33 +164,13 @@ bool OctreeRenderer::renderOperation(OctreeElement* element, void* extraData) { return false; } -void OctreeRenderer::render(RenderArgs::RenderMode renderMode, - RenderArgs::RenderSide renderSide, - RenderArgs::DebugFlags renderDebugFlags) { - RenderArgs args(this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust(), - renderMode, renderSide, renderDebugFlags); +void OctreeRenderer::render(RenderArgs* renderArgs) { if (_tree) { + renderArgs->_renderer = this; _tree->lockForRead(); - _tree->recurseTreeWithOperation(renderOperation, &args); + _tree->recurseTreeWithOperation(renderOperation, renderArgs); _tree->unlock(); } - _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; - } void OctreeRenderer::clear() { diff --git a/libraries/octree/src/OctreeRenderer.h b/libraries/octree/src/OctreeRenderer.h index ca0914723f..98026b732c 100644 --- a/libraries/octree/src/OctreeRenderer.h +++ b/libraries/octree/src/OctreeRenderer.h @@ -51,9 +51,7 @@ public: virtual void init(); /// render the content of the octree - 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); ViewFrustum* getViewFrustum() const { return _viewFrustum; } void setViewFrustum(ViewFrustum* viewFrustum) { _viewFrustum = viewFrustum; } @@ -62,23 +60,6 @@ public: /// clears the tree virtual void clear(); - - int getElementsTouched() const { return _elementsTouched; } - int getItemsRendered() const { return _itemsRendered; } - int getItemsOutOfView() const { return _itemsOutOfView; } - int getItemsTooSmall() const { return _itemsTooSmall; } - - int getMeshesConsidered() const { return _meshesConsidered; } - int getMeshesRendered() const { return _meshesRendered; } - int getMeshesOutOfView() const { return _meshesOutOfView; } - int getMeshesTooSmall() const { return _meshesTooSmall; } - - int getMaterialSwitches() const { return _materialSwitches; } - int getTrianglesRendered() const { return _trianglesRendered; } - int getQuadsRendered() const { return _quadsRendered; } - - int getTranslucentMeshPartsRendered() const { return _translucentMeshPartsRendered; } - int getOpaqueMeshPartsRendered() const { return _opaqueMeshPartsRendered; } protected: virtual Octree* createTree() = 0; @@ -86,23 +67,6 @@ protected: Octree* _tree; bool _managedTree; ViewFrustum* _viewFrustum; - - int _elementsTouched; - int _itemsRendered; - int _itemsOutOfView; - int _itemsTooSmall; - - int _meshesConsidered; - int _meshesRendered; - int _meshesOutOfView; - int _meshesTooSmall; - - int _materialSwitches; - int _trianglesRendered; - int _quadsRendered; - - int _translucentMeshPartsRendered; - int _opaqueMeshPartsRendered; }; #endif // hifi_OctreeRenderer_h diff --git a/libraries/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt index 9b2bb87b80..373279b299 100644 --- a/libraries/render-utils/CMakeLists.txt +++ b/libraries/render-utils/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME render-utils) -AUTOSCRIBE_SHADER_LIB(gpu model) +AUTOSCRIBE_SHADER_LIB(gpu model render) # pull in the resources.qrc file qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc") @@ -12,16 +12,16 @@ add_dependency_external_projects(glm) find_package(GLM REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) -if (WIN32) - if (USE_NSIGHT) - # try to find the Nsight package and add it to the build if we find it - find_package(NSIGHT) - if (NSIGHT_FOUND) - include_directories(${NSIGHT_INCLUDE_DIRS}) - add_definitions(-DNSIGHT_FOUND) - target_link_libraries(${TARGET_NAME} "${NSIGHT_LIBRARIES}") - endif () +if (WIN32) + if (USE_NSIGHT) + # try to find the Nsight package and add it to the build if we find it + find_package(NSIGHT) + if (NSIGHT_FOUND) + include_directories(${NSIGHT_INCLUDE_DIRS}) + add_definitions(-DNSIGHT_FOUND) + target_link_libraries(${TARGET_NAME} "${NSIGHT_LIBRARIES}") + endif () endif() endif (WIN32) -link_hifi_libraries(animation fbx shared gpu) \ No newline at end of file +link_hifi_libraries(animation fbx shared gpu model render) diff --git a/libraries/render-utils/src/AbstractViewStateInterface.h b/libraries/render-utils/src/AbstractViewStateInterface.h index a0cd103804..a2ed7dfbfd 100644 --- a/libraries/render-utils/src/AbstractViewStateInterface.h +++ b/libraries/render-utils/src/AbstractViewStateInterface.h @@ -15,6 +15,9 @@ #include #include +#include +#include + #include class Transform; @@ -56,6 +59,10 @@ public: virtual void postLambdaEvent(std::function f) = 0; virtual qreal getDevicePixelRatio() = 0; + virtual render::ScenePointer getMain3DScene() = 0; + virtual render::EnginePointer getRenderEngine() = 0; + + // FIXME - we shouldn't assume that there's a single instance of an AbstractViewStateInterface static AbstractViewStateInterface* instance(); static void setInstance(AbstractViewStateInterface* instance); }; diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index ff1c965b44..c4cfd7d6ea 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -12,7 +12,6 @@ // include this before QOpenGLFramebufferObject, which includes an earlier version of OpenGL #include - #include #include #include @@ -29,6 +28,7 @@ #include "simple_vert.h" #include "simple_frag.h" +#include "simple_textured_frag.h" #include "deferred_light_vert.h" #include "deferred_light_limited_vert.h" @@ -48,16 +48,30 @@ #include "point_light_frag.h" #include "spot_light_frag.h" +static const std::string glowIntensityShaderHandle = "glowIntensity"; + void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) { + auto VS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(simple_vert))); + auto PS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(simple_frag))); + auto PSTextured = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(simple_textured_frag))); + + gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PS)); + gpu::ShaderPointer programTextured = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PSTextured)); + + gpu::Shader::BindingSet slotBindings; + gpu::Shader::makeProgram(*program, slotBindings); + gpu::Shader::makeProgram(*programTextured, slotBindings); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setCullMode(gpu::State::CULL_BACK); + state->setDepthTest(true, true, gpu::LESS_EQUAL); + state->setBlendFunction(false, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + _simpleProgram = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); + _simpleProgramTextured = gpu::PipelinePointer(gpu::Pipeline::create(programTextured, state)); + _viewState = viewState; - _simpleProgram.addShaderFromSourceCode(QGLShader::Vertex, simple_vert); - _simpleProgram.addShaderFromSourceCode(QGLShader::Fragment, simple_frag); - _simpleProgram.link(); - - _simpleProgram.bind(); - _glowIntensityLocation = _simpleProgram.uniformLocation("glowIntensity"); - _simpleProgram.release(); - loadLightProgram(directional_light_frag, false, _directionalLight, _directionalLightLocations); loadLightProgram(directional_light_shadow_map_frag, false, _directionalLightShadowMap, _directionalLightShadowMapLocations); @@ -92,55 +106,56 @@ void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) { lp->setAmbientSpherePreset(gpu::SphericalHarmonics::Preset(_ambientLightMode % gpu::SphericalHarmonics::NUM_PRESET)); } -void DeferredLightingEffect::bindSimpleProgram() { - DependencyManager::get()->setPrimaryDrawBuffers(true, true, true); - _simpleProgram.bind(); - _simpleProgram.setUniformValue(_glowIntensityLocation, DependencyManager::get()->getIntensity()); - glDisable(GL_BLEND); +void DeferredLightingEffect::bindSimpleProgram(gpu::Batch& batch, bool textured) { + // DependencyManager::get()->setPrimaryDrawBuffers(batch, true, true, true); + + if (textured) { + batch.setPipeline(_simpleProgramTextured); + } else { + batch.setPipeline(_simpleProgram); + } } -void DeferredLightingEffect::releaseSimpleProgram() { - glEnable(GL_BLEND); - _simpleProgram.release(); - DependencyManager::get()->setPrimaryDrawBuffers(true, false, false); +void DeferredLightingEffect::releaseSimpleProgram(gpu::Batch& batch) { + // DependencyManager::get()->setPrimaryDrawBuffers(batch, true, false, false); } -void DeferredLightingEffect::renderSolidSphere(float radius, int slices, int stacks, const glm::vec4& color) { - bindSimpleProgram(); - DependencyManager::get()->renderSphere(radius, slices, stacks, color); - releaseSimpleProgram(); +void DeferredLightingEffect::renderSolidSphere(gpu::Batch& batch, float radius, int slices, int stacks, const glm::vec4& color) { + bindSimpleProgram(batch); + DependencyManager::get()->renderSphere(batch, radius, slices, stacks, color); + releaseSimpleProgram(batch); } -void DeferredLightingEffect::renderWireSphere(float radius, int slices, int stacks, const glm::vec4& color) { - bindSimpleProgram(); - DependencyManager::get()->renderSphere(radius, slices, stacks, color, false); - releaseSimpleProgram(); +void DeferredLightingEffect::renderWireSphere(gpu::Batch& batch, float radius, int slices, int stacks, const glm::vec4& color) { + bindSimpleProgram(batch); + DependencyManager::get()->renderSphere(batch, radius, slices, stacks, color, false); + releaseSimpleProgram(batch); } -void DeferredLightingEffect::renderSolidCube(float size, const glm::vec4& color) { - bindSimpleProgram(); - DependencyManager::get()->renderSolidCube(size, color); - releaseSimpleProgram(); +void DeferredLightingEffect::renderSolidCube(gpu::Batch& batch, float size, const glm::vec4& color) { + bindSimpleProgram(batch); + DependencyManager::get()->renderSolidCube(batch, size, color); + releaseSimpleProgram(batch); } -void DeferredLightingEffect::renderWireCube(float size, const glm::vec4& color) { - bindSimpleProgram(); - DependencyManager::get()->renderWireCube(size, color); - releaseSimpleProgram(); +void DeferredLightingEffect::renderWireCube(gpu::Batch& batch, float size, const glm::vec4& color) { + bindSimpleProgram(batch); + DependencyManager::get()->renderWireCube(batch, size, color); + releaseSimpleProgram(batch); } -void DeferredLightingEffect::renderLine(const glm::vec3& p1, const glm::vec3& p2, +void DeferredLightingEffect::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, const glm::vec3& maxCorner, + const glm::vec4& color) { + bindSimpleProgram(batch); + DependencyManager::get()->renderQuad(batch, minCorner, maxCorner, color); + releaseSimpleProgram(batch); +} + +void DeferredLightingEffect::renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, const glm::vec4& color1, const glm::vec4& color2) { - bindSimpleProgram(); - DependencyManager::get()->renderLine(p1, p2, color1, color2); - releaseSimpleProgram(); -} - - -void DeferredLightingEffect::renderSolidCone(float base, float height, int slices, int stacks) { - bindSimpleProgram(); - DependencyManager::get()->renderCone(base, height, slices, stacks); - releaseSimpleProgram(); + bindSimpleProgram(batch); + DependencyManager::get()->renderLine(batch, p1, p2, color1, color2); + releaseSimpleProgram(batch); } void DeferredLightingEffect::addPointLight(const glm::vec3& position, float radius, const glm::vec3& color, @@ -204,6 +219,7 @@ void DeferredLightingEffect::render() { QSize framebufferSize = textureCache->getFrameBufferSize(); + // binding the first framebuffer auto freeFBO = DependencyManager::get()->getFreeFramebuffer(); glBindFramebuffer(GL_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(freeFBO)); @@ -475,11 +491,20 @@ void DeferredLightingEffect::render() { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0); + // glDisable(GL_FRAMEBUFFER_SRGB); + // End of the Lighting pass +} + +void DeferredLightingEffect::copyBack(RenderArgs* args) { + auto textureCache = DependencyManager::get(); + QSize framebufferSize = textureCache->getFrameBufferSize(); + + auto freeFBO = DependencyManager::get()->getFreeFramebuffer(); + //freeFBO->release(); glBindFramebuffer(GL_FRAMEBUFFER, 0); - // glDisable(GL_FRAMEBUFFER_SRGB); glDisable(GL_CULL_FACE); @@ -502,6 +527,18 @@ void DeferredLightingEffect::render() { glPushMatrix(); glLoadIdentity(); + int viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + const int VIEWPORT_X_INDEX = 0; + const int VIEWPORT_Y_INDEX = 1; + const int VIEWPORT_WIDTH_INDEX = 2; + const int VIEWPORT_HEIGHT_INDEX = 3; + + float sMin = viewport[VIEWPORT_X_INDEX] / (float)framebufferSize.width(); + float sWidth = viewport[VIEWPORT_WIDTH_INDEX] / (float)framebufferSize.width(); + float tMin = viewport[VIEWPORT_Y_INDEX] / (float)framebufferSize.height(); + float tHeight = viewport[VIEWPORT_HEIGHT_INDEX] / (float)framebufferSize.height(); + renderFullscreenQuad(sMin, sMin + sWidth, tMin, tMin + tHeight); glBindTexture(GL_TEXTURE_2D, 0); @@ -517,12 +554,11 @@ void DeferredLightingEffect::render() { glMatrixMode(GL_MODELVIEW); glPopMatrix(); - - // now render the objects we held back until after deferred lighting - foreach (PostLightingRenderable* renderable, _postLightingRenderables) { - renderable->renderPostLighting(); - } - _postLightingRenderables.clear(); +} + +void DeferredLightingEffect::setupTransparent(RenderArgs* args, int lightBufferUnit) { + auto globalLight = _allocatedLights[_globalLights.front()]; + args->_batch->setUniformBuffer(lightBufferUnit, globalLight->getSchemaBuffer()); } void DeferredLightingEffect::loadLightProgram(const char* fragSource, bool limited, ProgramObject& program, LightLocations& locations) { diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index fa37d48221..bd7fcb286b 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -23,7 +23,7 @@ #include "model/Stage.h" class AbstractViewStateInterface; -class PostLightingRenderable; +class RenderArgs; /// Handles deferred lighting for the bits that require it (voxels...) class DeferredLightingEffect : public Dependency { @@ -33,33 +33,30 @@ public: void init(AbstractViewStateInterface* viewState); - /// Returns a reference to a simple program suitable for rendering static untextured geometry - ProgramObject& getSimpleProgram() { return _simpleProgram; } - /// Sets up the state necessary to render static untextured geometry with the simple program. - void bindSimpleProgram(); + void bindSimpleProgram(gpu::Batch& batch, bool textured = false); /// Tears down the state necessary to render static untextured geometry with the simple program. - void releaseSimpleProgram(); + void releaseSimpleProgram(gpu::Batch& batch); //// Renders a solid sphere with the simple program. - void renderSolidSphere(float radius, int slices, int stacks, const glm::vec4& color); + void renderSolidSphere(gpu::Batch& batch, float radius, int slices, int stacks, const glm::vec4& color); //// Renders a wireframe sphere with the simple program. - void renderWireSphere(float radius, int slices, int stacks, const glm::vec4& color); + void renderWireSphere(gpu::Batch& batch, float radius, int slices, int stacks, const glm::vec4& color); //// Renders a solid cube with the simple program. - void renderSolidCube(float size, const glm::vec4& color); + void renderSolidCube(gpu::Batch& batch, float size, const glm::vec4& color); //// Renders a wireframe cube with the simple program. - void renderWireCube(float size, const glm::vec4& color); + void renderWireCube(gpu::Batch& batch, float size, const glm::vec4& color); + + //// Renders a quad with the simple program. + void renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, const glm::vec3& maxCorner, const glm::vec4& color); //// Renders a line with the simple program. - void renderLine(const glm::vec3& p1, const glm::vec3& p2, + void renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, const glm::vec4& color1, const glm::vec4& color2); - - //// Renders a solid cone with the simple program. - void renderSolidCone(float base, float height, int slices, int stacks); /// Adds a point light to render for the current frame. void addPointLight(const glm::vec3& position, float radius, const glm::vec3& color = glm::vec3(0.0f, 0.0f, 0.0f), @@ -69,11 +66,11 @@ public: void addSpotLight(const glm::vec3& position, float radius, const glm::vec3& color = glm::vec3(1.0f, 1.0f, 1.0f), float intensity = 0.5f, const glm::quat& orientation = glm::quat(), float exponent = 0.0f, float cutoff = PI); - /// Adds an object to render after performing the deferred lighting for the current frame (e.g., a translucent object). - void addPostLightingRenderable(PostLightingRenderable* renderable) { _postLightingRenderables.append(renderable); } - void prepare(); void render(); + void copyBack(RenderArgs* args); + + void setupTransparent(RenderArgs* args, int lightBufferUnit); // update global lighting void setAmbientLightMode(int preset); @@ -101,10 +98,10 @@ private: }; static void loadLightProgram(const char* fragSource, bool limited, ProgramObject& program, LightLocations& locations); - - ProgramObject _simpleProgram; - int _glowIntensityLocation; + gpu::PipelinePointer _simpleProgram; + gpu::PipelinePointer _simpleProgramTextured; + ProgramObject _directionalSkyboxLight; LightLocations _directionalSkyboxLightLocations; ProgramObject _directionalSkyboxLightShadowMap; @@ -156,7 +153,6 @@ private: std::vector _globalLights; std::vector _pointLights; std::vector _spotLights; - QVector _postLightingRenderables; AbstractViewStateInterface* _viewState; @@ -165,10 +161,4 @@ private: model::SkyboxPointer _skybox; }; -/// Simple interface for objects that require something to be rendered after deferred lighting. -class PostLightingRenderable { -public: - virtual void renderPostLighting() = 0; -}; - #endif // hifi_DeferredLightingEffect_h diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 132ebbc914..303d63bef8 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -56,6 +56,12 @@ const int NUM_BYTES_PER_VERTEX = NUM_COORDS_PER_VERTEX * sizeof(GLfloat); const int NUM_BYTES_PER_INDEX = sizeof(GLushort); void GeometryCache::renderSphere(float radius, int slices, int stacks, const glm::vec4& color, bool solid, int id) { + gpu::Batch batch; + renderSphere(batch, radius, slices, stacks, color, solid, id); + gpu::GLBackend::renderBatch(batch); +} + +void GeometryCache::renderSphere(gpu::Batch& batch, float radius, int slices, int stacks, const glm::vec4& color, bool solid, int id) { bool registered = (id != UNKNOWN_ID); Vec2Pair radiusKey(glm::vec2(radius, slices), glm::vec2(stacks, 0)); @@ -279,8 +285,6 @@ void GeometryCache::renderSphere(float radius, int slices, int stacks, const glm gpu::BufferView normalsView(verticesBuffer, streamFormat->getAttributes().at(gpu::Stream::NORMAL)._element); gpu::BufferView colorView(colorBuffer, streamFormat->getAttributes().at(gpu::Stream::COLOR)._element); - gpu::Batch batch; - batch.setInputFormat(streamFormat); batch.setInputBuffer(VERTICES_SLOT, verticesView); batch.setInputBuffer(NORMALS_SLOT, normalsView); @@ -292,14 +296,6 @@ void GeometryCache::renderSphere(float radius, int slices, int stacks, const glm } else { batch.drawIndexed(gpu::LINES, indices); } - - gpu::GLBackend::renderBatch(batch); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } void GeometryCache::renderCone(float base, float height, int slices, int stacks) { @@ -402,7 +398,14 @@ void GeometryCache::renderCone(float base, float height, int slices, int stacks) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } + void GeometryCache::renderGrid(int xDivisions, int yDivisions, const glm::vec4& color) { + gpu::Batch batch; + renderGrid(batch, xDivisions, yDivisions, color); + gpu::GLBackend::renderBatch(batch); +} + +void GeometryCache::renderGrid(gpu::Batch& batch, int xDivisions, int yDivisions, const glm::vec4& color) { IntPair key(xDivisions, yDivisions); Vec3Pair colorKey(glm::vec3(color.x, color.y, yDivisions), glm::vec3(color.z, color.y, xDivisions)); @@ -470,25 +473,21 @@ void GeometryCache::renderGrid(int xDivisions, int yDivisions, const glm::vec4& gpu::BufferView verticesView(verticesBuffer, 0, verticesBuffer->getSize(), streamFormat->getAttributes().at(gpu::Stream::POSITION)._element); gpu::BufferView colorView(colorBuffer, streamFormat->getAttributes().at(gpu::Stream::COLOR)._element); - gpu::Batch batch; - batch.setInputFormat(streamFormat); batch.setInputBuffer(VERTICES_SLOT, verticesView); batch.setInputBuffer(COLOR_SLOT, colorView); batch.draw(gpu::LINES, vertices, 0); +} +void GeometryCache::renderGrid(int x, int y, int width, int height, int rows, int cols, const glm::vec4& color, int id) { + gpu::Batch batch; + renderGrid(batch, x, y, width, height, rows, cols, color, id); gpu::GLBackend::renderBatch(batch); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - } // TODO: properly handle the x,y,w,h changing for an ID // TODO: why do we seem to create extra BatchItemDetails when we resize the window?? what's that?? -void GeometryCache::renderGrid(int x, int y, int width, int height, int rows, int cols, const glm::vec4& color, int id) { +void GeometryCache::renderGrid(gpu::Batch& batch, int x, int y, int width, int height, int rows, int cols, const glm::vec4& color, int id) { #ifdef WANT_DEBUG qCDebug(renderutils) << "GeometryCache::renderGrid(x["<getSize(), streamFormat->getAttributes().at(gpu::Stream::POSITION)._element); gpu::BufferView colorView(colorBuffer, streamFormat->getAttributes().at(gpu::Stream::COLOR)._element); - - gpu::Batch batch; batch.setInputFormat(streamFormat); batch.setInputBuffer(VERTICES_SLOT, verticesView); batch.setInputBuffer(COLOR_SLOT, colorView); batch.draw(gpu::LINES, vertices, 0); - - gpu::GLBackend::renderBatch(batch); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - - glBindBuffer(GL_ARRAY_BUFFER, 0); } void GeometryCache::updateVertices(int id, const QVector& points, const glm::vec4& color) { @@ -791,24 +781,27 @@ void GeometryCache::updateVertices(int id, const QVector& points, con } void GeometryCache::renderVertices(gpu::Primitive primitiveType, int id) { + gpu::Batch batch; + renderVertices(batch, primitiveType, id); + gpu::GLBackend::renderBatch(batch); +} + +void GeometryCache::renderVertices(gpu::Batch& batch, gpu::Primitive primitiveType, int id) { BatchItemDetails& details = _registeredVertices[id]; if (details.isCreated) { - gpu::Batch batch; - batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); batch.draw(primitiveType, details.vertices, 0); - - gpu::GLBackend::renderBatch(batch); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - - glBindBuffer(GL_ARRAY_BUFFER, 0); } } void GeometryCache::renderSolidCube(float size, const glm::vec4& color) { + gpu::Batch batch; + renderSolidCube(batch, size, color); + gpu::GLBackend::renderBatch(batch); +} + +void GeometryCache::renderSolidCube(gpu::Batch& batch, float size, const glm::vec4& color) { Vec2Pair colorKey(glm::vec2(color.x, color.y), glm::vec2(color.z, color.y)); const int FLOATS_PER_VERTEX = 3; const int VERTICES_PER_FACE = 4; @@ -821,9 +814,9 @@ void GeometryCache::renderSolidCube(float size, const glm::vec4& color) { const int VERTEX_STRIDE = sizeof(GLfloat) * FLOATS_PER_VERTEX * 2; // vertices and normals const int NORMALS_OFFSET = sizeof(GLfloat) * FLOATS_PER_VERTEX; - if (!_solidCubeVerticies.contains(size)) { + if (!_solidCubeVertices.contains(size)) { gpu::BufferPointer verticesBuffer(new gpu::Buffer()); - _solidCubeVerticies[size] = verticesBuffer; + _solidCubeVertices[size] = verticesBuffer; GLfloat* vertexData = new GLfloat[vertexPoints * 2]; // vertices and normals GLfloat* vertex = vertexData; @@ -898,7 +891,7 @@ void GeometryCache::renderSolidCube(float size, const glm::vec4& color) { colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); } - gpu::BufferPointer verticesBuffer = _solidCubeVerticies[size]; + gpu::BufferPointer verticesBuffer = _solidCubeVertices[size]; gpu::BufferPointer colorBuffer = _solidCubeColors[colorKey]; const int VERTICES_SLOT = 0; @@ -914,25 +907,21 @@ void GeometryCache::renderSolidCube(float size, const glm::vec4& color) { gpu::BufferView normalsView(verticesBuffer, NORMALS_OFFSET, verticesBuffer->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::NORMAL)._element); gpu::BufferView colorView(colorBuffer, streamFormat->getAttributes().at(gpu::Stream::COLOR)._element); - gpu::Batch batch; - batch.setInputFormat(streamFormat); batch.setInputBuffer(VERTICES_SLOT, verticesView); batch.setInputBuffer(NORMALS_SLOT, normalsView); batch.setInputBuffer(COLOR_SLOT, colorView); batch.setIndexBuffer(gpu::UINT8, _solidCubeIndexBuffer, 0); batch.drawIndexed(gpu::TRIANGLES, indices); - - gpu::GLBackend::renderBatch(batch); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } void GeometryCache::renderWireCube(float size, const glm::vec4& color) { + gpu::Batch batch; + renderWireCube(batch, size, color); + gpu::GLBackend::renderBatch(batch); +} + +void GeometryCache::renderWireCube(gpu::Batch& batch, float size, const glm::vec4& color) { Vec2Pair colorKey(glm::vec2(color.x, color.y),glm::vec2(color.z, color.y)); const int FLOATS_PER_VERTEX = 3; const int VERTICES_PER_EDGE = 2; @@ -1002,24 +991,20 @@ void GeometryCache::renderWireCube(float size, const glm::vec4& color) { gpu::BufferView verticesView(verticesBuffer, streamFormat->getAttributes().at(gpu::Stream::POSITION)._element); gpu::BufferView colorView(colorBuffer, streamFormat->getAttributes().at(gpu::Stream::COLOR)._element); - gpu::Batch batch; - batch.setInputFormat(streamFormat); batch.setInputBuffer(VERTICES_SLOT, verticesView); batch.setInputBuffer(COLOR_SLOT, colorView); batch.setIndexBuffer(gpu::UINT8, _wireCubeIndexBuffer, 0); batch.drawIndexed(gpu::LINES, indices); - - gpu::GLBackend::renderBatch(batch); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } void GeometryCache::renderBevelCornersRect(int x, int y, int width, int height, int bevelDistance, const glm::vec4& color, int id) { + gpu::Batch batch; + renderBevelCornersRect(batch, x, y, width, height, bevelDistance, color, id); + gpu::GLBackend::renderBatch(batch); +} + +void GeometryCache::renderBevelCornersRect(gpu::Batch& batch, int x, int y, int width, int height, int bevelDistance, const glm::vec4& color, int id) { bool registered = (id != UNKNOWN_ID); Vec3Pair key(glm::vec3(x, y, 0.0f), glm::vec3(width, height, bevelDistance)); BatchItemDetails& details = registered ? _registeredBevelRects[id] : _bevelRects[key]; @@ -1112,21 +1097,18 @@ void GeometryCache::renderBevelCornersRect(int x, int y, int width, int height, delete[] vertexBuffer; } - gpu::Batch batch; - batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); batch.draw(gpu::QUADS, 4, 0); - - gpu::GLBackend::renderBatch(batch); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - - glBindBuffer(GL_ARRAY_BUFFER, 0); } void GeometryCache::renderQuad(const glm::vec2& minCorner, const glm::vec2& maxCorner, const glm::vec4& color, int id) { + gpu::Batch batch; + renderQuad(batch, minCorner, maxCorner, color, id); + gpu::GLBackend::renderBatch(batch); +} + +void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner, const glm::vec4& color, int id) { bool registered = (id != UNKNOWN_ID); Vec4Pair key(glm::vec4(minCorner.x, minCorner.y, maxCorner.x, maxCorner.y), color); BatchItemDetails& details = registered ? _registeredQuad2D[id] : _quad2D[key]; @@ -1192,23 +1174,22 @@ void GeometryCache::renderQuad(const glm::vec2& minCorner, const glm::vec2& maxC details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); } - gpu::Batch batch; - batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); batch.draw(gpu::QUADS, 4, 0); - - gpu::GLBackend::renderBatch(batch); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - - glBindBuffer(GL_ARRAY_BUFFER, 0); } void GeometryCache::renderQuad(const glm::vec2& minCorner, const glm::vec2& maxCorner, const glm::vec2& texCoordMinCorner, const glm::vec2& texCoordMaxCorner, const glm::vec4& color, int id) { + gpu::Batch batch; + renderQuad(batch, minCorner, maxCorner, texCoordMinCorner, texCoordMaxCorner, color, id); + gpu::GLBackend::renderBatch(batch); +} + +void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner, + const glm::vec2& texCoordMinCorner, const glm::vec2& texCoordMaxCorner, + const glm::vec4& color, int id) { bool registered = (id != UNKNOWN_ID); Vec4PairVec4 key(Vec4Pair(glm::vec4(minCorner.x, minCorner.y, maxCorner.x, maxCorner.y), @@ -1281,27 +1262,18 @@ void GeometryCache::renderQuad(const glm::vec2& minCorner, const glm::vec2& maxC details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); } - gpu::Batch batch; - -// glEnable(GL_TEXTURE_2D); - //glBindTexture(GL_TEXTURE_2D, _currentTextureID); // this is quad specific... - batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); batch.draw(gpu::QUADS, 4, 0); - - gpu::GLBackend::renderBatch(batch); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - // glBindTexture(GL_TEXTURE_2D, 0); - // glDisable(GL_TEXTURE_2D); } void GeometryCache::renderQuad(const glm::vec3& minCorner, const glm::vec3& maxCorner, const glm::vec4& color, int id) { + gpu::Batch batch; + renderQuad(batch, minCorner, maxCorner, color, id); + gpu::GLBackend::renderBatch(batch); +} + +void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, const glm::vec3& maxCorner, const glm::vec4& color, int id) { bool registered = (id != UNKNOWN_ID); Vec3PairVec4 key(Vec3Pair(minCorner, maxCorner), color); BatchItemDetails& details = registered ? _registeredQuad3D[id] : _quad3D[key]; @@ -1367,18 +1339,9 @@ void GeometryCache::renderQuad(const glm::vec3& minCorner, const glm::vec3& maxC details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); } - gpu::Batch batch; - batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); batch.draw(gpu::QUADS, 4, 0); - - gpu::GLBackend::renderBatch(batch); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - - glBindBuffer(GL_ARRAY_BUFFER, 0); } void GeometryCache::renderQuad(const glm::vec3& topLeft, const glm::vec3& bottomLeft, @@ -1386,6 +1349,17 @@ void GeometryCache::renderQuad(const glm::vec3& topLeft, const glm::vec3& bottom const glm::vec2& texCoordTopLeft, const glm::vec2& texCoordBottomLeft, const glm::vec2& texCoordBottomRight, const glm::vec2& texCoordTopRight, const glm::vec4& color, int id) { + gpu::Batch batch; + renderQuad(batch, topLeft, bottomLeft, bottomRight, topRight, texCoordTopLeft, texCoordBottomLeft, + texCoordBottomRight, texCoordTopRight, color, id); + gpu::GLBackend::renderBatch(batch); +} + +void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, const glm::vec3& bottomLeft, + const glm::vec3& bottomRight, const glm::vec3& topRight, + const glm::vec2& texCoordTopLeft, const glm::vec2& texCoordBottomLeft, + const glm::vec2& texCoordBottomRight, const glm::vec2& texCoordTopRight, + const glm::vec4& color, int id) { #ifdef WANT_DEBUG qCDebug(renderutils) << "renderQuad() vec3 + texture VBO..."; @@ -1469,31 +1443,18 @@ void GeometryCache::renderQuad(const glm::vec3& topLeft, const glm::vec3& bottom details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); } - gpu::Batch batch; - - glEnable(GL_TEXTURE_2D); - //glBindTexture(GL_TEXTURE_2D, _currentTextureID); // this is quad specific... - batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); batch.draw(gpu::QUADS, 4, 0); - - gpu::GLBackend::renderBatch(batch); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - - // TODO: The callers of this method (renderMagnifier and renderReticle) assume that we won't disable an unbind - // the texture after rendering. I'm not sure if this is correct in general but it's currently required for the - // oculus overlay to work. - //glBindTexture(GL_TEXTURE_2D, 0); - //glDisable(GL_TEXTURE_2D); } void GeometryCache::renderDashedLine(const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, int id) { + gpu::Batch batch; + renderDashedLine(batch, start, end, color, id); + gpu::GLBackend::renderBatch(batch); +} + +void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, int id) { bool registered = (id != UNKNOWN_ID); Vec3PairVec2Pair key(Vec3Pair(start, end), Vec2Pair(glm::vec2(color.x, color.y), glm::vec2(color.z, color.w))); BatchItemDetails& details = registered ? _registeredDashedLines[id] : _dashedLines[key]; @@ -1593,18 +1554,9 @@ void GeometryCache::renderDashedLine(const glm::vec3& start, const glm::vec3& en #endif } - gpu::Batch batch; - batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); batch.draw(gpu::LINES, details.vertices, 0); - - gpu::GLBackend::renderBatch(batch); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - - glBindBuffer(GL_ARRAY_BUFFER, 0); } @@ -1658,6 +1610,13 @@ void GeometryCache::BatchItemDetails::clear() { void GeometryCache::renderLine(const glm::vec3& p1, const glm::vec3& p2, const glm::vec4& color1, const glm::vec4& color2, int id) { + gpu::Batch batch; + renderLine(batch, p1, p2, color1, color2, id); + gpu::GLBackend::renderBatch(batch); +} + +void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, + const glm::vec4& color1, const glm::vec4& color2, int id) { bool registered = (id != UNKNOWN_ID); Vec3Pair key(p1, p2); @@ -1734,21 +1693,19 @@ void GeometryCache::renderLine(const glm::vec3& p1, const glm::vec3& p2, #endif } - gpu::Batch batch; - // this is what it takes to render a quad batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); batch.draw(gpu::LINES, 2, 0); - - gpu::GLBackend::renderBatch(batch); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - glBindBuffer(GL_ARRAY_BUFFER, 0); } -void GeometryCache::renderLine(const glm::vec2& p1, const glm::vec2& p2, +void GeometryCache::renderLine(const glm::vec2& p1, const glm::vec2& p2, const glm::vec4& color1, const glm::vec4& color2, int id) { + gpu::Batch batch; + renderLine(batch, p1, p2, color1, color2, id); + gpu::GLBackend::renderBatch(batch); +} + +void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, const glm::vec4& color1, const glm::vec4& color2, int id) { bool registered = (id != UNKNOWN_ID); @@ -1826,18 +1783,10 @@ void GeometryCache::renderLine(const glm::vec2& p1, const glm::vec2& p2, #endif } - gpu::Batch batch; - // this is what it takes to render a quad batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); batch.draw(gpu::LINES, 2, 0); - - gpu::GLBackend::renderBatch(batch); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - glBindBuffer(GL_ARRAY_BUFFER, 0); } diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 672e4ddb78..b438eb2d3b 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -135,70 +135,113 @@ public: void renderCone(float base, float height, int slices, int stacks); - void renderSphere(float radius, int slices, int stacks, const glm::vec3& color, bool solid = true, int id = UNKNOWN_ID) + void renderSphere(float radius, int slices, int stacks, const glm::vec3& color, bool solid = true, int id = UNKNOWN_ID) { renderSphere(radius, slices, stacks, glm::vec4(color, 1.0f), solid, id); } + void renderSphere(gpu::Batch& batch, float radius, int slices, int stacks, const glm::vec3& color, bool solid = true, int id = UNKNOWN_ID) + { renderSphere(batch, radius, slices, stacks, glm::vec4(color, 1.0f), solid, id); } void renderSphere(float radius, int slices, int stacks, const glm::vec4& color, bool solid = true, int id = UNKNOWN_ID); + void renderSphere(gpu::Batch& batch, float radius, int slices, int stacks, const glm::vec4& color, bool solid = true, int id = UNKNOWN_ID); + void renderGrid(int xDivisions, int yDivisions, const glm::vec4& color); + void renderGrid(gpu::Batch& batch, int xDivisions, int yDivisions, const glm::vec4& color); void renderGrid(int x, int y, int width, int height, int rows, int cols, const glm::vec4& color, int id = UNKNOWN_ID); + void renderGrid(gpu::Batch& batch, int x, int y, int width, int height, int rows, int cols, const glm::vec4& color, int id = UNKNOWN_ID); + void renderSolidCube(float size, const glm::vec4& color); + void renderSolidCube(gpu::Batch& batch, float size, const glm::vec4& color); void renderWireCube(float size, const glm::vec4& color); + void renderWireCube(gpu::Batch& batch, float size, const glm::vec4& color); void renderBevelCornersRect(int x, int y, int width, int height, int bevelDistance, const glm::vec4& color, int id = UNKNOWN_ID); + void renderBevelCornersRect(gpu::Batch& batch, int x, int y, int width, int height, int bevelDistance, const glm::vec4& color, int id = UNKNOWN_ID); void renderQuad(int x, int y, int width, int height, const glm::vec4& color, int id = UNKNOWN_ID) { renderQuad(glm::vec2(x,y), glm::vec2(x + width, y + height), color, id); } + void renderQuad(gpu::Batch& batch, int x, int y, int width, int height, const glm::vec4& color, int id = UNKNOWN_ID) + { renderQuad(batch, glm::vec2(x,y), glm::vec2(x + width, y + height), color, id); } // TODO: I think there's a bug in this version of the renderQuad() that's not correctly rebuilding the vbos // if the color changes by the corners are the same, as evidenced by the audio meter which should turn white // when it's clipping void renderQuad(const glm::vec2& minCorner, const glm::vec2& maxCorner, const glm::vec4& color, int id = UNKNOWN_ID); + void renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner, const glm::vec4& color, int id = UNKNOWN_ID); void renderQuad(const glm::vec2& minCorner, const glm::vec2& maxCorner, const glm::vec2& texCoordMinCorner, const glm::vec2& texCoordMaxCorner, const glm::vec4& color, int id = UNKNOWN_ID); + void renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner, + const glm::vec2& texCoordMinCorner, const glm::vec2& texCoordMaxCorner, + const glm::vec4& color, int id = UNKNOWN_ID); void renderQuad(const glm::vec3& minCorner, const glm::vec3& maxCorner, const glm::vec4& color, int id = UNKNOWN_ID); + void renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, const glm::vec3& maxCorner, const glm::vec4& color, int id = UNKNOWN_ID); void renderQuad(const glm::vec3& topLeft, const glm::vec3& bottomLeft, const glm::vec3& bottomRight, const glm::vec3& topRight, const glm::vec2& texCoordTopLeft, const glm::vec2& texCoordBottomLeft, const glm::vec2& texCoordBottomRight, const glm::vec2& texCoordTopRight, const glm::vec4& color, int id = UNKNOWN_ID); + void renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, const glm::vec3& bottomLeft, + const glm::vec3& bottomRight, const glm::vec3& topRight, + const glm::vec2& texCoordTopLeft, const glm::vec2& texCoordBottomLeft, + const glm::vec2& texCoordBottomRight, const glm::vec2& texCoordTopRight, + const glm::vec4& color, int id = UNKNOWN_ID); void renderLine(const glm::vec3& p1, const glm::vec3& p2, const glm::vec3& color, int id = UNKNOWN_ID) { renderLine(p1, p2, color, color, id); } + void renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, const glm::vec3& color, int id = UNKNOWN_ID) + { renderLine(batch, p1, p2, color, color, id); } void renderLine(const glm::vec3& p1, const glm::vec3& p2, const glm::vec3& color1, const glm::vec3& color2, int id = UNKNOWN_ID) { renderLine(p1, p2, glm::vec4(color1, 1.0f), glm::vec4(color2, 1.0f), id); } + void renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, + const glm::vec3& color1, const glm::vec3& color2, int id = UNKNOWN_ID) + { renderLine(batch, p1, p2, glm::vec4(color1, 1.0f), glm::vec4(color2, 1.0f), id); } void renderLine(const glm::vec3& p1, const glm::vec3& p2, const glm::vec4& color, int id = UNKNOWN_ID) { renderLine(p1, p2, color, color, id); } + void renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, + const glm::vec4& color, int id = UNKNOWN_ID) + { renderLine(batch, p1, p2, color, color, id); } void renderLine(const glm::vec3& p1, const glm::vec3& p2, const glm::vec4& color1, const glm::vec4& color2, int id = UNKNOWN_ID); + void renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, + const glm::vec4& color1, const glm::vec4& color2, int id = UNKNOWN_ID); void renderDashedLine(const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, int id = UNKNOWN_ID); + void renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, int id = UNKNOWN_ID); void renderLine(const glm::vec2& p1, const glm::vec2& p2, const glm::vec3& color, int id = UNKNOWN_ID) { renderLine(p1, p2, glm::vec4(color, 1.0f), id); } + void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, const glm::vec3& color, int id = UNKNOWN_ID) + { renderLine(batch, p1, p2, glm::vec4(color, 1.0f), id); } void renderLine(const glm::vec2& p1, const glm::vec2& p2, const glm::vec4& color, int id = UNKNOWN_ID) { renderLine(p1, p2, color, color, id); } + void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, const glm::vec4& color, int id = UNKNOWN_ID) + { renderLine(batch, p1, p2, color, color, id); } void renderLine(const glm::vec2& p1, const glm::vec2& p2, const glm::vec3& color1, const glm::vec3& color2, int id = UNKNOWN_ID) { renderLine(p1, p2, glm::vec4(color1, 1.0f), glm::vec4(color2, 1.0f), id); } + void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, + const glm::vec3& color1, const glm::vec3& color2, int id = UNKNOWN_ID) + { renderLine(batch, p1, p2, glm::vec4(color1, 1.0f), glm::vec4(color2, 1.0f), id); } void renderLine(const glm::vec2& p1, const glm::vec2& p2, const glm::vec4& color1, const glm::vec4& color2, int id = UNKNOWN_ID); + void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, + const glm::vec4& color1, const glm::vec4& color2, int id = UNKNOWN_ID); void updateVertices(int id, const QVector& points, const glm::vec4& color); void updateVertices(int id, const QVector& points, const glm::vec4& color); void updateVertices(int id, const QVector& points, const QVector& texCoords, const glm::vec4& color); + void renderVertices(gpu::Batch& batch, gpu::Primitive primitiveType, int id); void renderVertices(gpu::Primitive primitiveType, int id); /// Loads geometry from the specified URL. @@ -227,7 +270,7 @@ private: QHash _cubeColors; gpu::BufferPointer _wireCubeIndexBuffer; - QHash _solidCubeVerticies; + QHash _solidCubeVertices; QHash _solidCubeColors; gpu::BufferPointer _solidCubeIndexBuffer; diff --git a/libraries/render-utils/src/GlowEffect.cpp b/libraries/render-utils/src/GlowEffect.cpp index 07c2e0e9c6..2e8b58c6c2 100644 --- a/libraries/render-utils/src/GlowEffect.cpp +++ b/libraries/render-utils/src/GlowEffect.cpp @@ -94,7 +94,7 @@ void GlowEffect::init(bool enabled) { _enabled = enabled; } -void GlowEffect::prepare() { +void GlowEffect::prepare(RenderArgs* renderArgs) { auto primaryFBO = DependencyManager::get()->getPrimaryFramebuffer(); GLuint fbo = gpu::GLBackend::getFramebufferID(primaryFBO); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); @@ -105,19 +105,19 @@ void GlowEffect::prepare() { _isOddFrame = !_isOddFrame; } -void GlowEffect::begin(float intensity) { +void GlowEffect::begin(RenderArgs* renderArgs, float intensity) { // store the current intensity and add the new amount _intensityStack.push(_intensity); glBlendColor(0.0f, 0.0f, 0.0f, _intensity += intensity); _isEmpty &= (_intensity == 0.0f); } -void GlowEffect::end() { +void GlowEffect::end(RenderArgs* renderArgs) { // restore the saved intensity glBlendColor(0.0f, 0.0f, 0.0f, _intensity = _intensityStack.pop()); } -gpu::FramebufferPointer GlowEffect::render() { +gpu::FramebufferPointer GlowEffect::render(RenderArgs* renderArgs) { PerformanceTimer perfTimer("glowEffect"); auto textureCache = DependencyManager::get(); @@ -212,10 +212,14 @@ void GlowEffect::toggleGlowEffect(bool enabled) { } Glower::Glower(float amount) { - DependencyManager::get()->begin(amount); + RenderArgs renderArgs; + DependencyManager::get()->begin(&renderArgs, amount); +} +Glower::Glower(RenderArgs* renderArgs, float amount) : _renderArgs(renderArgs) { + DependencyManager::get()->begin(_renderArgs, amount); } Glower::~Glower() { - DependencyManager::get()->end(); + DependencyManager::get()->end(_renderArgs); } diff --git a/libraries/render-utils/src/GlowEffect.h b/libraries/render-utils/src/GlowEffect.h index 73c512ecf5..23a21a9575 100644 --- a/libraries/render-utils/src/GlowEffect.h +++ b/libraries/render-utils/src/GlowEffect.h @@ -15,6 +15,8 @@ #include #include +#include "RenderArgs.h" + #include #include #include @@ -37,14 +39,14 @@ public: void init(bool enabled); /// Prepares the glow effect for rendering the current frame. To be called before rendering the scene. - void prepare(); + void prepare(RenderArgs* renderArgs); /// Starts using the glow effect. /// \param intensity the desired glow intensity, from zero to one - void begin(float intensity = 1.0f); + void begin(RenderArgs* renderArgs, float intensity = 1.0f); /// Stops using the glow effect. - void end(); + void end(RenderArgs* renderArgs); /// Returns the current glow intensity. float getIntensity() const { return _intensity; } @@ -52,7 +54,7 @@ public: /// Renders the glow effect. To be called after rendering the scene. /// \param toTexture whether to render to a texture, rather than to the frame buffer /// \return the framebuffer object to which we rendered, or NULL if to the frame buffer - gpu::FramebufferPointer render(); + gpu::FramebufferPointer render(RenderArgs* renderArgs); public slots: void toggleGlowEffect(bool enabled); @@ -85,7 +87,11 @@ class Glower { public: Glower(float amount = 1.0f); + Glower(RenderArgs* renderArgs, float amount = 1.0f); ~Glower(); + +private: + RenderArgs* _renderArgs; }; #endif // hifi_GlowEffect_h diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 483ea6177e..2ae8968571 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -79,8 +79,10 @@ Model::Model(QObject* parent) : _lodDistance(0.0f), _pupilDilation(0.0f), _url("http://invalid.com"), + _isVisible(true), _blendNumber(0), _appliedBlendNumber(0), + _calculatedMeshPartBoxesValid(false), _calculatedMeshBoxesValid(false), _calculatedMeshTrianglesValid(false), _meshGroupsKnown(false), @@ -91,6 +93,8 @@ Model::Model(QObject* parent) : if (_viewState) { moveToThread(_viewState->getMainThread()); } + + setSnapModelToRegistrationPoint(true, glm::vec3(0.5f)); } Model::~Model() { @@ -110,6 +114,7 @@ void Model::RenderPipelineLib::addRenderPipeline(Model::RenderKey key, slotBindings.insert(gpu::Shader::Binding(std::string("normalMap"), 1)); slotBindings.insert(gpu::Shader::Binding(std::string("specularMap"), 2)); slotBindings.insert(gpu::Shader::Binding(std::string("emissiveMap"), 3)); + slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), 4)); gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vertexShader, pixelShader)); gpu::Shader::makeProgram(*program, slotBindings); @@ -135,7 +140,7 @@ void Model::RenderPipelineLib::addRenderPipeline(Model::RenderKey key, // Blend on transparent state->setBlendFunction(key.isTranslucent(), - gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, // For transparent only, this keep the highlight intensity gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); // Good to go add the brand new pipeline @@ -192,13 +197,17 @@ void Model::RenderPipelineLib::initLocations(gpu::ShaderPointer& program, Model: #if (GPU_FEATURE_PROFILE == GPU_CORE) locations.materialBufferUnit = program->getBuffers().findLocation("materialBuffer"); + locations.lightBufferUnit = program->getBuffers().findLocation("lightBuffer"); #else locations.materialBufferUnit = program->getUniforms().findLocation("materialBuffer"); + locations.lightBufferUnit = program->getUniforms().findLocation("lightBuffer"); #endif locations.clusterMatrices = program->getUniforms().findLocation("clusterMatrices"); locations.clusterIndices = program->getInputs().findLocation("clusterIndices");; locations.clusterWeights = program->getInputs().findLocation("clusterWeights");; + + } @@ -309,10 +318,14 @@ void Model::init() { RenderKey(RenderKey::HAS_TANGENTS | RenderKey::HAS_SPECULAR), modelNormalMapVertex, modelNormalSpecularMapPixel); - + _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::IS_TRANSLUCENT), - modelVertex, modelTranslucentPixel); + RenderKey(RenderKey::IS_TRANSLUCENT), + modelVertex, modelTranslucentPixel); + // FIXME Ignore lightmap for translucents meshpart + _renderPipelineLib.addRenderPipeline( + RenderKey(RenderKey::IS_TRANSLUCENT | RenderKey::HAS_LIGHTMAP), + modelVertex, modelTranslucentPixel); _renderPipelineLib.addRenderPipeline( RenderKey(RenderKey::HAS_TANGENTS | RenderKey::IS_TRANSLUCENT), @@ -401,6 +414,8 @@ void Model::reset() { } _meshGroupsKnown = false; + _readyWhenAdded = false; // in case any of our users are using scenes + invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid } bool Model::updateGeometry() { @@ -451,6 +466,8 @@ bool Model::updateGeometry() { _dilatedTextures.clear(); _geometry = geometry; _meshGroupsKnown = false; + _readyWhenAdded = false; // in case any of our users are using scenes + invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid initJointStates(newJointStates); needToRebuild = true; } else if (_jointStates.isEmpty()) { @@ -666,12 +683,14 @@ bool Model::convexHullContains(glm::vec3 point) { void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { bool calculatedMeshTrianglesNeeded = pickAgainstTriangles && !_calculatedMeshTrianglesValid; - if (!_calculatedMeshBoxesValid || calculatedMeshTrianglesNeeded) { + if (!_calculatedMeshBoxesValid || calculatedMeshTrianglesNeeded || (!_calculatedMeshPartBoxesValid && pickAgainstTriangles) ) { const FBXGeometry& geometry = _geometry->getFBXGeometry(); int numberOfMeshes = geometry.meshes.size(); _calculatedMeshBoxes.resize(numberOfMeshes); _calculatedMeshTriangles.clear(); _calculatedMeshTriangles.resize(numberOfMeshes); + _calculatedMeshPartBoxes.clear(); + _calculatedMeshPartOffet.clear(); for (int i = 0; i < numberOfMeshes; i++) { const FBXMesh& mesh = geometry.meshes.at(i); Extents scaledMeshExtents = calculateScaledOffsetExtents(mesh.meshExtents); @@ -680,9 +699,13 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { if (pickAgainstTriangles) { QVector thisMeshTriangles; + qint64 partOffset = 0; for (int j = 0; j < mesh.parts.size(); j++) { const FBXMeshPart& part = mesh.parts.at(j); + bool atLeastOnePointInBounds = false; + AABox thisPartBounds; + const int INDICES_PER_TRIANGLE = 3; const int INDICES_PER_QUAD = 4; @@ -695,10 +718,26 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { int i2 = part.quadIndices[vIndex++]; int i3 = part.quadIndices[vIndex++]; - glm::vec3 v0 = calculateScaledOffsetPoint(glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i0], 1.0f))); - glm::vec3 v1 = calculateScaledOffsetPoint(glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i1], 1.0f))); - glm::vec3 v2 = calculateScaledOffsetPoint(glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i2], 1.0f))); - glm::vec3 v3 = calculateScaledOffsetPoint(glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i3], 1.0f))); + glm::vec3 mv0 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i0], 1.0f)); + glm::vec3 mv1 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i1], 1.0f)); + glm::vec3 mv2 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i2], 1.0f)); + glm::vec3 mv3 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i3], 1.0f)); + + // track the mesh parts in model space + if (!atLeastOnePointInBounds) { + thisPartBounds.setBox(mv0, 0.0f); + atLeastOnePointInBounds = true; + } else { + thisPartBounds += mv0; + } + thisPartBounds += mv1; + thisPartBounds += mv2; + thisPartBounds += mv3; + + glm::vec3 v0 = calculateScaledOffsetPoint(mv0); + glm::vec3 v1 = calculateScaledOffsetPoint(mv1); + glm::vec3 v2 = calculateScaledOffsetPoint(mv2); + glm::vec3 v3 = calculateScaledOffsetPoint(mv3); // Sam's recommended triangle slices Triangle tri1 = { v0, v1, v3 }; @@ -710,6 +749,7 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { thisMeshTriangles.push_back(tri1); thisMeshTriangles.push_back(tri2); + } } @@ -721,17 +761,38 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { int i1 = part.triangleIndices[vIndex++]; int i2 = part.triangleIndices[vIndex++]; - glm::vec3 v0 = calculateScaledOffsetPoint(glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i0], 1.0f))); - glm::vec3 v1 = calculateScaledOffsetPoint(glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i1], 1.0f))); - glm::vec3 v2 = calculateScaledOffsetPoint(glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i2], 1.0f))); + glm::vec3 mv0 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i0], 1.0f)); + glm::vec3 mv1 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i1], 1.0f)); + glm::vec3 mv2 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i2], 1.0f)); + + // track the mesh parts in model space + if (!atLeastOnePointInBounds) { + thisPartBounds.setBox(mv0, 0.0f); + atLeastOnePointInBounds = true; + } else { + thisPartBounds += mv0; + } + thisPartBounds += mv1; + thisPartBounds += mv2; + + glm::vec3 v0 = calculateScaledOffsetPoint(mv0); + glm::vec3 v1 = calculateScaledOffsetPoint(mv1); + glm::vec3 v2 = calculateScaledOffsetPoint(mv2); Triangle tri = { v0, v1, v2 }; thisMeshTriangles.push_back(tri); } } + _calculatedMeshPartBoxes[QPair(i, j)] = thisPartBounds; + _calculatedMeshPartOffet[QPair(i, j)] = partOffset; + + partOffset += part.quadIndices.size() * sizeof(int); + partOffset += part.triangleIndices.size() * sizeof(int); + } _calculatedMeshTriangles[i] = thisMeshTriangles; + _calculatedMeshPartBoxesValid = true; } } _calculatedMeshBoxesValid = true; @@ -765,27 +826,163 @@ void Model::renderSetup(RenderArgs* args) { } } -bool Model::render(float alpha, RenderMode mode, RenderArgs* args) { + +class TransparentMeshPart { +public: + TransparentMeshPart(Model* model, int meshIndex, int partIndex) : model(model), meshIndex(meshIndex), partIndex(partIndex) { } + typedef render::Payload Payload; + typedef Payload::DataPointer Pointer; + + Model* model; + int meshIndex; + int partIndex; +}; + +namespace render { + template <> const ItemKey payloadGetKey(const TransparentMeshPart::Pointer& payload) { + if (!payload->model->isVisible()) { + return ItemKey::Builder().withInvisible().build(); + } + return ItemKey::Builder::transparentShape(); + } + + template <> const Item::Bound payloadGetBound(const TransparentMeshPart::Pointer& payload) { + if (payload) { + return payload->model->getPartBounds(payload->meshIndex, payload->partIndex); + } + return render::Item::Bound(); + } + template <> void payloadRender(const TransparentMeshPart::Pointer& payload, RenderArgs* args) { + if (args) { + return payload->model->renderPart(args, payload->meshIndex, payload->partIndex, true); + } + } +} + +class OpaqueMeshPart { +public: + OpaqueMeshPart(Model* model, int meshIndex, int partIndex) : model(model), meshIndex(meshIndex), partIndex(partIndex) { } + typedef render::Payload Payload; + typedef Payload::DataPointer Pointer; + + Model* model; + int meshIndex; + int partIndex; +}; + +namespace render { + template <> const ItemKey payloadGetKey(const OpaqueMeshPart::Pointer& payload) { + if (!payload->model->isVisible()) { + return ItemKey::Builder().withInvisible().build(); + } + return ItemKey::Builder::opaqueShape(); + } + + template <> const Item::Bound payloadGetBound(const OpaqueMeshPart::Pointer& payload) { + if (payload) { + Item::Bound result = payload->model->getPartBounds(payload->meshIndex, payload->partIndex); + //qDebug() << "payloadGetBound(OpaqueMeshPart) " << result; + return result; + } + return render::Item::Bound(); + } + template <> void payloadRender(const OpaqueMeshPart::Pointer& payload, RenderArgs* args) { + if (args) { + return payload->model->renderPart(args, payload->meshIndex, payload->partIndex, false); + } + } + /* template <> const model::MaterialKey& shapeGetMaterialKey(const OpaqueMeshPart::Pointer& payload) { + return payload->model->getPartMaterial(payload->meshIndex, payload->partIndex); + }*/ +} + +void Model::setVisibleInScene(bool newValue, std::shared_ptr scene) { + if (_isVisible != newValue) { + _isVisible = newValue; + + render::PendingChanges pendingChanges; + foreach (auto item, _renderItems.keys()) { + pendingChanges.resetItem(item, _renderItems[item]); + } + scene->enqueuePendingChanges(pendingChanges); + } +} + + +bool Model::addToScene(std::shared_ptr scene, render::PendingChanges& pendingChanges) { + if (!_meshGroupsKnown && isLoadedWithTextures()) { + segregateMeshGroups(); + } + + bool somethingAdded = false; + + // allow the attachments to add to scene + foreach (Model* attachment, _attachments) { + bool attachementSomethingAdded = attachment->addToScene(scene, pendingChanges); + somethingAdded = somethingAdded || attachementSomethingAdded; + } + + foreach (auto renderItem, _transparentRenderItems) { + auto item = scene->allocateID(); + auto renderData = TransparentMeshPart::Pointer(renderItem); + auto renderPayload = render::PayloadPointer(new TransparentMeshPart::Payload(renderData)); + pendingChanges.resetItem(item, renderPayload); + _renderItems.insert(item, renderPayload); + somethingAdded = true; + } + foreach (auto renderItem, _opaqueRenderItems) { + auto item = scene->allocateID(); + auto renderData = OpaqueMeshPart::Pointer(renderItem); + auto renderPayload = render::PayloadPointer(new OpaqueMeshPart::Payload(renderData)); + pendingChanges.resetItem(item, renderPayload); + _renderItems.insert(item, renderPayload); + somethingAdded = true; + } + + _readyWhenAdded = readyToAddToScene(); + + return somethingAdded; +} + +void Model::removeFromScene(std::shared_ptr scene, render::PendingChanges& pendingChanges) { + // allow the attachments to remove to scene + foreach (Model* attachment, _attachments) { + attachment->removeFromScene(scene, pendingChanges); + } + + foreach (auto item, _renderItems.keys()) { + pendingChanges.removeItem(item); + } + _renderItems.clear(); + _readyWhenAdded = false; +} + +bool Model::render(RenderArgs* renderArgs, float alpha) { + return true; // PROFILE_RANGE(__FUNCTION__); // render the attachments foreach (Model* attachment, _attachments) { - attachment->render(alpha, mode, args); + attachment->render(renderArgs, alpha); } if (_meshStates.isEmpty()) { return false; } - renderSetup(args); - return renderCore(alpha, mode, args); + renderSetup(renderArgs); + return renderCore(renderArgs, alpha); } -bool Model::renderCore(float alpha, RenderMode mode, RenderArgs* args) { +bool Model::renderCore(RenderArgs* args, float alpha) { + return true; + PROFILE_RANGE(__FUNCTION__); if (!_viewState) { return false; } + auto mode = args->_renderMode; + // Let's introduce a gpu::Batch to capture all the calls to the graphics api _renderBatch.clear(); gpu::Batch& batch = _renderBatch; @@ -946,8 +1143,8 @@ bool Model::renderCore(float alpha, RenderMode mode, RenderArgs* args) { glPushMatrix(); #endif - ::gpu::GLBackend::renderBatch(batch); - + ::gpu::GLBackend::renderBatch(batch, true); // force sync with gl state here + #if defined(ANDROID) #else glPopMatrix(); @@ -957,11 +1154,6 @@ bool Model::renderCore(float alpha, RenderMode mode, RenderArgs* args) { // restore all the default material settings _viewState->setupWorldLight(); - if (args) { - args->_translucentMeshPartsRendered = translucentMeshPartsRendered; - args->_opaqueMeshPartsRendered = opaqueMeshPartsRendered; - } - #ifdef WANT_DEBUG_MESHBOXES renderDebugMeshBoxes(); #endif @@ -1078,6 +1270,12 @@ Extents Model::calculateScaledOffsetExtents(const Extents& extents) const { return translatedExtents; } +/// Returns the world space equivalent of some box in model space. +AABox Model::calculateScaledOffsetAABox(const AABox& box) const { + + return AABox(calculateScaledOffsetExtents(Extents(box))); +} + glm::vec3 Model::calculateScaledOffsetPoint(const glm::vec3& point) const { // we need to include any fst scaling, translation, and rotation, which is captured in the offset matrix glm::vec3 offsetPoint = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(point, 1.0f)); @@ -1143,6 +1341,10 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo if (_url == url && _geometry && _geometry->getURL() == url) { return; } + + _readyWhenAdded = false; // reset out render items. + _needsReload = true; + _url = url; // if so instructed, keep the current geometry until the new one is loaded @@ -1398,10 +1600,17 @@ void Model::snapToRegistrationPoint() { } void Model::simulate(float deltaTime, bool fullUpdate) { + /* + qDebug() << "Model::simulate()"; + qDebug() << " _translation:" << _translation; + qDebug() << " _rotation:" << _rotation; + */ + fullUpdate = updateGeometry() || fullUpdate || (_scaleToFit && !_scaledToFit) || (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint); if (isActive() && fullUpdate) { + // NOTE: this seems problematic... need to review _calculatedMeshBoxesValid = false; // if we have to simulate, we need to assume our mesh boxes are all invalid _calculatedMeshTrianglesValid = false; @@ -1788,6 +1997,9 @@ void Model::applyNextGeometry() { _baseGeometry = _nextBaseGeometry; _geometry = _nextGeometry; _meshGroupsKnown = false; + _readyWhenAdded = false; // in case any of our users are using scenes + _needsReload = false; // we are loaded now! + invalidCalculatedMeshBoxes(); _nextBaseGeometry.reset(); _nextGeometry.reset(); } @@ -1819,15 +2031,6 @@ void Model::deleteGeometry() { _blendedBlendshapeCoefficients.clear(); } -// Scene rendering support -QVector Model::_modelsInScene; -gpu::Batch Model::_sceneRenderBatch; -void Model::startScene(RenderArgs::RenderSide renderSide) { - if (renderSide != RenderArgs::STEREO_RIGHT) { - _modelsInScene.clear(); - } -} - void Model::setupBatchTransform(gpu::Batch& batch, RenderArgs* args) { // Capture the view matrix once for the rendering of this model @@ -1844,223 +2047,239 @@ void Model::setupBatchTransform(gpu::Batch& batch, RenderArgs* args) { batch.setViewTransform(_transforms[0]); } -void Model::endScene(RenderMode mode, RenderArgs* args) { - PROFILE_RANGE(__FUNCTION__); - - -#if (GPU_TRANSFORM_PROFILE == GPU_LEGACY) - // with legacy transform profile, we still to protect that transform stack... - glPushMatrix(); -#endif - - RenderArgs::RenderSide renderSide = RenderArgs::MONO; - if (args) { - renderSide = args->_renderSide; +AABox Model::getPartBounds(int meshIndex, int partIndex) { + if (!_calculatedMeshPartBoxesValid) { + recalculateMeshBoxes(true); } - - gpu::GLBackend backend; - - if (args) { - glm::mat4 proj; - // If for easier debug depending on the pass - if (mode == RenderArgs::SHADOW_RENDER_MODE) { - args->_viewFrustum->evalProjectionMatrix(proj); - } else { - args->_viewFrustum->evalProjectionMatrix(proj); - } - gpu::Batch batch; - batch.setProjectionTransform(proj); - backend.render(batch); + if (_calculatedMeshPartBoxesValid && _calculatedMeshPartBoxes.contains(QPair(meshIndex, partIndex))) { + return calculateScaledOffsetAABox(_calculatedMeshPartBoxes[QPair(meshIndex, partIndex)]); } - - // Do the rendering batch creation for mono or left eye, not for right eye - if (renderSide != RenderArgs::STEREO_RIGHT) { - // Let's introduce a gpu::Batch to capture all the calls to the graphics api - _sceneRenderBatch.clear(); - gpu::Batch& batch = _sceneRenderBatch; - - /*DependencyManager::get()->setPrimaryDrawBuffers( - mode == RenderArgs::DEFAULT_RENDER_MODE || mode == RenderArgs::DIFFUSE_RENDER_MODE, - mode == RenderArgs::DEFAULT_RENDER_MODE || mode == RenderArgs::NORMAL_RENDER_MODE, - mode == RenderArgs::DEFAULT_RENDER_MODE); - */ - - /* if (mode != RenderArgs::SHADOW_RENDER_MODE) */{ - GLenum buffers[3]; - - int bufferCount = 0; - // if (mode == RenderArgs::DEFAULT_RENDER_MODE || mode == RenderArgs::DIFFUSE_RENDER_MODE) { - if (mode != RenderArgs::SHADOW_RENDER_MODE) { - buffers[bufferCount++] = GL_COLOR_ATTACHMENT0; - } - //if (mode == RenderArgs::DEFAULT_RENDER_MODE || mode == RenderArgs::NORMAL_RENDER_MODE) { - if (mode != RenderArgs::SHADOW_RENDER_MODE) { - buffers[bufferCount++] = GL_COLOR_ATTACHMENT1; - } - // if (mode == RenderArgs::DEFAULT_RENDER_MODE) { - if (mode != RenderArgs::SHADOW_RENDER_MODE) { - buffers[bufferCount++] = GL_COLOR_ATTACHMENT2; - } - GLBATCH(glDrawBuffers)(bufferCount, buffers); - - // batch.setFramebuffer(DependencyManager::get()->getPrimaryOpaqueFramebuffer()); - } - - const float DEFAULT_ALPHA_THRESHOLD = 0.5f; - - int opaqueMeshPartsRendered = 0; - - // now, for each model in the scene, render the mesh portions - opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, false, false, args); - opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, true, false, args); - opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, true, false, false, args); - opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, true, true, false, args); - opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, false, false, false, args); - opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, false, true, false, args); - opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, true, false, false, args); - opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, true, true, false, args); - - opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, false, false, false, args); - opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, false, false, args); - opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, false, false, false, args); - opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, true, false, false, args); - - opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, false, true, args); - - // render translucent meshes afterwards - { - GLenum buffers[2]; - int bufferCount = 0; - buffers[bufferCount++] = GL_COLOR_ATTACHMENT1; - buffers[bufferCount++] = GL_COLOR_ATTACHMENT2; - GLBATCH(glDrawBuffers)(bufferCount, buffers); - } - - int translucentParts = 0; - const float MOSTLY_OPAQUE_THRESHOLD = 0.75f; - translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, false, false, args); - translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, true, false, args); - translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, false, false, args); - translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, true, false, args); - translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, false, false, args); - translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, true, false, args); - translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, false, false, args); - translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, true, false, args); - - - { - GLenum buffers[1]; - int bufferCount = 0; - buffers[bufferCount++] = GL_COLOR_ATTACHMENT0; - GLBATCH(glDrawBuffers)(bufferCount, buffers); - // batch.setFramebuffer(DependencyManager::get()->getPrimaryTransparentFramebuffer()); - } - - // if (mode == RenderArgs::DEFAULT_RENDER_MODE || mode == RenderArgs::DIFFUSE_RENDER_MODE) { - if (mode != RenderArgs::SHADOW_RENDER_MODE) { - // batch.setFramebuffer(DependencyManager::get()->getPrimaryTransparentFramebuffer()); - - const float MOSTLY_TRANSPARENT_THRESHOLD = 0.0f; - translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, false, false, args); - translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, true, false, args); - translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, false, false, args); - translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, true, false, args); - translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, false, false, args); - translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, true, false, args); - translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, false, false, args); - translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, true, false, args); - - // batch.setFramebuffer(DependencyManager::get()->getPrimaryOpaqueFramebuffer()); - } - - GLBATCH(glDepthMask)(true); - GLBATCH(glDepthFunc)(GL_LESS); - GLBATCH(glDisable)(GL_CULL_FACE); - - if (mode == RenderArgs::SHADOW_RENDER_MODE) { - GLBATCH(glCullFace)(GL_BACK); - } - - - GLBATCH(glActiveTexture)(GL_TEXTURE0 + 1); - GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0); - GLBATCH(glActiveTexture)(GL_TEXTURE0 + 2); - GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0); - GLBATCH(glActiveTexture)(GL_TEXTURE0 + 3); - GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0); - GLBATCH(glActiveTexture)(GL_TEXTURE0); - GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0); - - - // deactivate vertex arrays after drawing - GLBATCH(glDisableClientState)(GL_NORMAL_ARRAY); - GLBATCH(glDisableClientState)(GL_VERTEX_ARRAY); - GLBATCH(glDisableClientState)(GL_TEXTURE_COORD_ARRAY); - GLBATCH(glDisableClientState)(GL_COLOR_ARRAY); - GLBATCH(glDisableVertexAttribArray)(gpu::Stream::TANGENT); - GLBATCH(glDisableVertexAttribArray)(gpu::Stream::SKIN_CLUSTER_INDEX); - GLBATCH(glDisableVertexAttribArray)(gpu::Stream::SKIN_CLUSTER_WEIGHT); - - // bind with 0 to switch back to normal operation - GLBATCH(glBindBuffer)(GL_ARRAY_BUFFER, 0); - GLBATCH(glBindBuffer)(GL_ELEMENT_ARRAY_BUFFER, 0); - GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0); - - // Back to no program - GLBATCH(glUseProgram)(0); - - if (args) { - args->_translucentMeshPartsRendered = translucentParts; - args->_opaqueMeshPartsRendered = opaqueMeshPartsRendered; - } - - } - - // Render! - { - PROFILE_RANGE("render Batch"); - backend.render(_sceneRenderBatch); - } - - -#if (GPU_TRANSFORM_PROFILE == GPU_LEGACY) - // with legacy transform profile, we still to protect that transform stack... - glPopMatrix(); -#endif - - // restore all the default material settings - _viewState->setupWorldLight(); - + return AABox(); } -bool Model::renderInScene(float alpha, RenderArgs* args) { - // render the attachments - foreach (Model* attachment, _attachments) { - attachment->renderInScene(alpha); - } - if (_meshStates.isEmpty()) { - return false; - } - - if (args->_debugFlags == RenderArgs::RENDER_DEBUG_HULLS && _renderCollisionHull == false) { - // turning collision hull rendering on - _renderCollisionHull = true; - _nextGeometry = _collisionGeometry; - _saveNonCollisionGeometry = _geometry; - updateGeometry(); - simulate(0.0, true); - } else if (args->_debugFlags != RenderArgs::RENDER_DEBUG_HULLS && _renderCollisionHull == true) { - // turning collision hull rendering off - _renderCollisionHull = false; - _nextGeometry = _saveNonCollisionGeometry; - _saveNonCollisionGeometry.clear(); - updateGeometry(); - simulate(0.0, true); +void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool translucent) { + if (!_readyWhenAdded) { + return; // bail asap } - renderSetup(args); - _modelsInScene.push_back(this); - return true; + // we always need these properly calculated before we can render, this will likely already have been done + // since the engine will call our getPartBounds() before rendering us. + if (!_calculatedMeshPartBoxesValid) { + recalculateMeshBoxes(true); + } + auto textureCache = DependencyManager::get(); + + gpu::Batch& batch = *(args->_batch); + auto mode = args->_renderMode; + + // render the part bounding box + #ifdef DEBUG_BOUNDING_PARTS + { + glm::vec4 cubeColor(1.0f,0.0f,0.0f,1.0f); + AABox partBounds = getPartBounds(meshIndex, partIndex); + + glm::mat4 translation = glm::translate(partBounds.calcCenter()); + glm::mat4 scale = glm::scale(partBounds.getDimensions()); + glm::mat4 modelToWorldMatrix = translation * scale; + batch.setModelTransform(modelToWorldMatrix); + //qDebug() << "partBounds:" << partBounds; + DependencyManager::get()->renderWireCube(batch, 1.0f, cubeColor); + } + #endif //def DEBUG_BOUNDING_PARTS + + // Capture the view matrix once for the rendering of this model + if (_transforms.empty()) { + _transforms.push_back(Transform()); + } + + auto alphaThreshold = args->_alphaThreshold; //translucent ? TRANSPARENT_ALPHA_THRESHOLD : OPAQUE_ALPHA_THRESHOLD; // FIX ME + + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const QVector& networkMeshes = _geometry->getMeshes(); + + // guard against partially loaded meshes + if (meshIndex >= networkMeshes.size() || meshIndex >= geometry.meshes.size() || meshIndex >= _meshStates.size() ) { + return; + } + + const NetworkMesh& networkMesh = networkMeshes.at(meshIndex); + const FBXMesh& mesh = geometry.meshes.at(meshIndex); + const MeshState& state = _meshStates.at(meshIndex); + + bool translucentMesh = translucent; // networkMesh.getTranslucentPartCount(mesh) == networkMesh.parts.size(); + bool hasTangents = !mesh.tangents.isEmpty(); + bool hasSpecular = mesh.hasSpecularTexture(); + bool hasLightmap = mesh.hasEmissiveTexture(); + bool isSkinned = state.clusterMatrices.size() > 1; + bool wireframe = isWireframe(); + + if (wireframe) { + translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false; + } + + Locations* locations = nullptr; + pickPrograms(batch, mode, translucentMesh, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe, + args, locations); + + updateVisibleJointStates(); + + // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown + // to false to rebuild out mesh groups. + + if (meshIndex < 0 || meshIndex >= networkMeshes.size() || meshIndex > geometry.meshes.size()) { + _meshGroupsKnown = false; // regenerate these lists next time around. + _readyWhenAdded = false; // in case any of our users are using scenes + invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid + return; // FIXME! + } + + batch.setIndexBuffer(gpu::UINT32, (networkMesh._indexBuffer), 0); + int vertexCount = mesh.vertices.size(); + if (vertexCount == 0) { + // sanity check + return; // FIXME! + } + + // Transform stage + if (_transforms.empty()) { + _transforms.push_back(Transform()); + } + + if (isSkinned) { + GLBATCH(glUniformMatrix4fv)(locations->clusterMatrices, state.clusterMatrices.size(), false, + (const float*)state.clusterMatrices.constData()); + _transforms[0] = Transform(); + _transforms[0].preTranslate(_translation); + } else { + _transforms[0] = Transform(state.clusterMatrices[0]); + _transforms[0].preTranslate(_translation); + } + batch.setModelTransform(_transforms[0]); + + if (mesh.blendshapes.isEmpty()) { + batch.setInputFormat(networkMesh._vertexFormat); + batch.setInputStream(0, *networkMesh._vertexStream); + } else { + batch.setInputFormat(networkMesh._vertexFormat); + batch.setInputBuffer(0, _blendedVertexBuffers[meshIndex], 0, sizeof(glm::vec3)); + batch.setInputBuffer(1, _blendedVertexBuffers[meshIndex], vertexCount * sizeof(glm::vec3), sizeof(glm::vec3)); + batch.setInputStream(2, *networkMesh._vertexStream); + } + + if (mesh.colors.isEmpty()) { + GLBATCH(glColor4f)(1.0f, 1.0f, 1.0f, 1.0f); + } + + // guard against partially loaded meshes + if (partIndex >= networkMesh.parts.size() || partIndex >= mesh.parts.size()) { + return; + } + + const NetworkMeshPart& networkPart = networkMesh.parts.at(partIndex); + const FBXMeshPart& part = mesh.parts.at(partIndex); + model::MaterialPointer material = part._material; + + if (material == nullptr) { + // qCDebug(renderutils) << "WARNING: material == nullptr!!!"; + } + + if (material != nullptr) { + + // apply material properties + if (mode != RenderArgs::SHADOW_RENDER_MODE) { + #ifdef WANT_DEBUG + qCDebug(renderutils) << "Material Changed ---------------------------------------------"; + qCDebug(renderutils) << "part INDEX:" << partIndex; + qCDebug(renderutils) << "NEW part.materialID:" << part.materialID; + #endif //def WANT_DEBUG + + if (locations->materialBufferUnit >= 0) { + batch.setUniformBuffer(locations->materialBufferUnit, material->getSchemaBuffer()); + } + + Texture* diffuseMap = networkPart.diffuseTexture.data(); + if (mesh.isEye && diffuseMap) { + // FIXME - guard against out of bounds here + if (meshIndex < _dilatedTextures.size()) { + if (partIndex < _dilatedTextures[meshIndex].size()) { + diffuseMap = (_dilatedTextures[meshIndex][partIndex] = + static_cast(diffuseMap)->getDilatedTexture(_pupilDilation)).data(); + } + } + } + static bool showDiffuse = true; + if (showDiffuse && diffuseMap) { + batch.setUniformTexture(0, diffuseMap->getGPUTexture()); + + } else { + batch.setUniformTexture(0, textureCache->getWhiteTexture()); + } + + if (locations->texcoordMatrices >= 0) { + glm::mat4 texcoordTransform[2]; + if (!part.diffuseTexture.transform.isIdentity()) { + part.diffuseTexture.transform.getMatrix(texcoordTransform[0]); + } + if (!part.emissiveTexture.transform.isIdentity()) { + part.emissiveTexture.transform.getMatrix(texcoordTransform[1]); + } + GLBATCH(glUniformMatrix4fv)(locations->texcoordMatrices, 2, false, (const float*) &texcoordTransform); + } + + if (!mesh.tangents.isEmpty()) { + Texture* normalMap = networkPart.normalTexture.data(); + batch.setUniformTexture(1, !normalMap ? + textureCache->getBlueTexture() : normalMap->getGPUTexture()); + + } + + if (locations->specularTextureUnit >= 0) { + Texture* specularMap = networkPart.specularTexture.data(); + batch.setUniformTexture(locations->specularTextureUnit, !specularMap ? + textureCache->getWhiteTexture() : specularMap->getGPUTexture()); + } + + if (args) { + args->_details._materialSwitches++; + } + + // HACK: For unknown reason (yet!) this code that should be assigned only if the material changes need to be called for every + // drawcall with an emissive, so let's do it for now. + if (locations->emissiveTextureUnit >= 0) { + // assert(locations->emissiveParams >= 0); // we should have the emissiveParams defined in the shader + float emissiveOffset = part.emissiveParams.x; + float emissiveScale = part.emissiveParams.y; + GLBATCH(glUniform2f)(locations->emissiveParams, emissiveOffset, emissiveScale); + + Texture* emissiveMap = networkPart.emissiveTexture.data(); + batch.setUniformTexture(locations->emissiveTextureUnit, !emissiveMap ? + textureCache->getWhiteTexture() : emissiveMap->getGPUTexture()); + } + + if (translucent && locations->lightBufferUnit >= 0) { + DependencyManager::get()->setupTransparent(args, locations->lightBufferUnit); + } + } + } + + qint64 offset = _calculatedMeshPartOffet[QPair(meshIndex, partIndex)]; + + if (part.quadIndices.size() > 0) { + batch.drawIndexed(gpu::QUADS, part.quadIndices.size(), offset); + offset += part.quadIndices.size() * sizeof(int); + } + + if (part.triangleIndices.size() > 0) { + batch.drawIndexed(gpu::TRIANGLES, part.triangleIndices.size(), offset); + offset += part.triangleIndices.size() * sizeof(int); + } + + if (args) { + const int INDICES_PER_TRIANGLE = 3; + const int INDICES_PER_QUAD = 4; + args->_details._trianglesRendered += part.triangleIndices.size() / INDICES_PER_TRIANGLE; + args->_details._quadsRendered += part.quadIndices.size() / INDICES_PER_QUAD; + } } void Model::segregateMeshGroups() { @@ -2081,6 +2300,7 @@ void Model::segregateMeshGroups() { const NetworkMesh& networkMesh = networkMeshes.at(i); const FBXMesh& mesh = geometry.meshes.at(i); const MeshState& state = _meshStates.at(i); + bool translucentMesh = networkMesh.getTranslucentPartCount(mesh) == networkMesh.parts.size(); bool hasTangents = !mesh.tangents.isEmpty(); @@ -2092,6 +2312,18 @@ void Model::segregateMeshGroups() { if (wireframe) { translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false; } + + // Debug... + int totalParts = mesh.parts.size(); + for (int partIndex = 0; partIndex < totalParts; partIndex++) { + // this is a good place to create our renderPayloads + if (translucentMesh) { + _transparentRenderItems << std::shared_ptr(new TransparentMeshPart(this, i, partIndex)); + } else { + _opaqueRenderItems << std::shared_ptr(new OpaqueMeshPart(this, i, partIndex)); + } + } + QString materialID; @@ -2170,34 +2402,6 @@ void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, f } } -int Model::renderMeshesForModelsInScene(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, - bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args) { - - PROFILE_RANGE(__FUNCTION__); - int meshPartsRendered = 0; - - bool pickProgramsNeeded = true; - Locations* locations = nullptr; - - foreach(Model* model, _modelsInScene) { - QVector* whichList = model->pickMeshList(translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe); - if (whichList) { - QVector& list = *whichList; - if (list.size() > 0) { - if (pickProgramsNeeded) { - pickPrograms(batch, mode, translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe, args, locations); - pickProgramsNeeded = false; - } - - model->setupBatchTransform(batch, args); - meshPartsRendered += model->renderMeshesFromList(list, batch, mode, translucent, alphaThreshold, args, locations); - } - } - } - - return meshPartsRendered; -} - int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args, bool forceRenderSomeMeshes) { @@ -2247,6 +2451,8 @@ int Model::renderMeshesFromList(QVector& list, gpu::Batch& batch, RenderMod if (i < 0 || i >= networkMeshes.size() || i > geometry.meshes.size()) { _meshGroupsKnown = false; // regenerate these lists next time around. + _readyWhenAdded = false; // in case any of our users are using scenes + invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid continue; } @@ -2264,8 +2470,6 @@ int Model::renderMeshesFromList(QVector& list, gpu::Batch& batch, RenderMod // if we got here, then check to see if this mesh is in view if (args) { bool shouldRender = true; - args->_meshesConsidered++; - if (args->_viewFrustum) { shouldRender = forceRenderMeshes || @@ -2275,17 +2479,10 @@ int Model::renderMeshesFromList(QVector& list, gpu::Batch& batch, RenderMod float distance = args->_viewFrustum->distanceToCamera(_calculatedMeshBoxes.at(i).calcCenter()); shouldRender = !_viewState ? false : _viewState->shouldRenderMesh(_calculatedMeshBoxes.at(i).getLargestDimension(), distance); - if (!shouldRender) { - args->_meshesTooSmall++; - } - } else { - args->_meshesOutOfView++; } } - if (shouldRender) { - args->_meshesRendered++; - } else { + if (!shouldRender) { continue; // skip this mesh } } @@ -2376,11 +2573,6 @@ int Model::renderMeshesFromList(QVector& list, gpu::Batch& batch, RenderMod batch.setUniformTexture(locations->specularTextureUnit, !specularMap ? textureCache->getWhiteTexture() : specularMap->getGPUTexture()); } - - if (args) { - args->_materialSwitches++; - } - } // HACK: For unkwon reason (yet!) this code that should be assigned only if the material changes need to be called for every @@ -2411,12 +2603,6 @@ int Model::renderMeshesFromList(QVector& list, gpu::Batch& batch, RenderMod offset += part.triangleIndices.size() * sizeof(int); } - if (args) { - const int INDICES_PER_TRIANGLE = 3; - const int INDICES_PER_QUAD = 4; - args->_trianglesRendered += part.triangleIndices.size() / INDICES_PER_TRIANGLE; - args->_quadsRendered += part.quadIndices.size() / INDICES_PER_QUAD; - } } } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 76a2d0855f..6f751a5f8d 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -30,6 +30,7 @@ #include #include #include "PhysicsEntity.h" +#include #include #include "AnimationHandle.h" @@ -45,6 +46,22 @@ class Shape; #include "RenderArgs.h" class ViewFrustum; +namespace render { + class Scene; + class PendingChanges; + typedef unsigned int ItemID; +} +class OpaqueMeshPart; +class TransparentMeshPart; + +inline uint qHash(const std::shared_ptr& a, uint seed) { + return qHash(a.get(), seed); +} +inline uint qHash(const std::shared_ptr& a, uint seed) { + return qHash(a.get(), seed); +} + + /// A generic 3D model displaying geometry loaded from a URL. class Model : public QObject, public PhysicsEntity { @@ -91,6 +108,9 @@ public: bool isActive() const { return _geometry && _geometry->isLoaded(); } bool isRenderable() const { return !_meshStates.isEmpty() || (isActive() && _geometry->getMeshes().isEmpty()); } + + void setVisibleInScene(bool newValue, std::shared_ptr scene); + bool isVisible() const { return _isVisible; } bool isLoadedWithTextures() const { return _geometry && _geometry->isLoadedWithTextures(); } @@ -98,12 +118,13 @@ public: void reset(); virtual void simulate(float deltaTime, bool fullUpdate = true); - bool render(float alpha = 1.0f, RenderArgs::RenderMode mode = RenderArgs::DEFAULT_RENDER_MODE, RenderArgs* args = NULL); + void renderSetup(RenderArgs* args); - // Scene rendering support - static void startScene(RenderArgs::RenderSide renderSide); - bool renderInScene(float alpha = 1.0f, RenderArgs* args = NULL); - static void endScene(RenderArgs::RenderMode mode = RenderArgs::DEFAULT_RENDER_MODE, RenderArgs* args = NULL); + // new Scene/Engine rendering support + bool needsFixupInScene() { return !_readyWhenAdded && readyToAddToScene(); } + bool readyToAddToScene(RenderArgs* renderArgs = nullptr) { return !_needsReload && isRenderable() && isActive() && isLoadedWithTextures(); } + bool addToScene(std::shared_ptr scene, render::PendingChanges& pendingChanges); + void removeFromScene(std::shared_ptr scene, render::PendingChanges& pendingChanges); /// Sets the URL of the model to render. /// \param fallback the URL of a fallback model to render if the requested model fails to load @@ -135,6 +156,9 @@ public: /// Returns the scaled equivalent of some extents in model space. Extents calculateScaledOffsetExtents(const Extents& extents) const; + /// Returns the world space equivalent of some box in model space. + AABox calculateScaledOffsetAABox(const AABox& box) const; + /// Returns the scaled equivalent of a point in model space. glm::vec3 calculateScaledOffsetPoint(const glm::vec3& point) const; @@ -219,6 +243,9 @@ public: BoxFace& face, QString& extraInfo, bool pickAgainstTriangles = false); bool convexHullContains(glm::vec3 point); + AABox getPartBounds(int meshIndex, int partIndex); + void renderPart(RenderArgs* args, int meshIndex, int partIndex, bool translucent); + protected: QSharedPointer _geometry; @@ -233,7 +260,7 @@ protected: bool _snapModelToRegistrationPoint; /// is the model's offset automatically adjusted to a registration point in model space bool _snappedToRegistrationPoint; /// are we currently snapped to a registration point - glm::vec3 _registrationPoint; /// the point in model space our center is snapped to + glm::vec3 _registrationPoint = glm::vec3(0.5f); /// the point in model space our center is snapped to bool _showTrueJointTransforms; @@ -285,7 +312,11 @@ protected: float getLimbLength(int jointIndex) const; /// Allow sub classes to force invalidating the bboxes - void invalidCalculatedMeshBoxes() { _calculatedMeshBoxesValid = false; } + void invalidCalculatedMeshBoxes() { + _calculatedMeshBoxesValid = false; + _calculatedMeshPartBoxesValid = false; + _calculatedMeshTrianglesValid = false; + } private: @@ -311,6 +342,7 @@ private: QUrl _url; QUrl _collisionUrl; + bool _isVisible; gpu::Buffers _blendedVertexBuffers; std::vector _transforms; @@ -341,8 +373,14 @@ private: int clusterMatrices; int clusterIndices; int clusterWeights; + int lightBufferUnit; }; + QHash, AABox> _calculatedMeshPartBoxes; // world coordinate AABoxes for all sub mesh part boxes + QHash, qint64> _calculatedMeshPartOffet; + + + bool _calculatedMeshPartBoxesValid; QVector _calculatedMeshBoxes; // world coordinate AABoxes for all sub mesh boxes bool _calculatedMeshBoxesValid; @@ -362,16 +400,8 @@ private: void renderDebugMeshBoxes(); int _debugMeshBoxesID = GeometryCache::UNKNOWN_ID; - // Scene rendering support - static QVector _modelsInScene; - static gpu::Batch _sceneRenderBatch; - - static void endSceneSimple(RenderArgs::RenderMode mode = RenderArgs::DEFAULT_RENDER_MODE, RenderArgs* args = NULL); - static void endSceneSplitPass(RenderArgs::RenderMode mode = RenderArgs::DEFAULT_RENDER_MODE, RenderArgs* args = NULL); - // helper functions used by render() or renderInScene() - void renderSetup(RenderArgs* args); - bool renderCore(float alpha, RenderArgs::RenderMode mode, RenderArgs* args); + bool renderCore(RenderArgs* args, float alpha); int renderMeshes(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, float alphaThreshold, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args = NULL, bool forceRenderMeshes = false); @@ -387,10 +417,6 @@ private: bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args, Locations*& locations); - static int renderMeshesForModelsInScene(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, float alphaThreshold, - bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args); - - static AbstractViewStateInterface* _viewState; class RenderKey { @@ -511,6 +537,21 @@ private: RenderBucketMap _renderBuckets; bool _renderCollisionHull; + + + QSet> _transparentRenderItems; + QSet> _opaqueRenderItems; + QMap _renderItems; + bool _readyWhenAdded = false; + bool _needsReload = true; + + +private: + // FIX ME - We want to get rid of this interface for rendering... + // right now the only remaining user are Avatar attachments. + // that usage has been temporarily disabled... + bool render(RenderArgs* renderArgs, float alpha = 1.0f); + }; Q_DECLARE_METATYPE(QPointer) diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp new file mode 100755 index 0000000000..777d9466a5 --- /dev/null +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -0,0 +1,230 @@ +// +// RenderDeferredTask.cpp +// render-utils/src/ +// +// Created by Sam Gateau on 5/29/15. +// Copyright 20154 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 "RenderDeferredTask.h" + +#include "gpu/Batch.h" +#include "gpu/Context.h" +#include "DeferredLightingEffect.h" +#include "ViewFrustum.h" +#include "RenderArgs.h" + +#include + + +using namespace render; + +template <> void render::jobRun(const PrepareDeferred& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + PerformanceTimer perfTimer("PrepareDeferred"); + DependencyManager::get()->prepare(); +} + +template <> void render::jobRun(const RenderDeferred& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + PerformanceTimer perfTimer("RenderDeferred"); + DependencyManager::get()->render(); +// renderContext->args->_context->syncCache(); +} + +template <> void render::jobRun(const ResolveDeferred& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + PerformanceTimer perfTimer("ResolveDeferred"); + DependencyManager::get()->copyBack(renderContext->args); + renderContext->args->_context->syncCache(); + +} + + + +RenderDeferredTask::RenderDeferredTask() : Task() { + _jobs.push_back(Job(PrepareDeferred())); + _jobs.push_back(Job(DrawBackground())); + _jobs.push_back(Job(DrawOpaqueDeferred())); + _jobs.push_back(Job(DrawLight())); + _jobs.push_back(Job(ResetGLState())); + _jobs.push_back(Job(RenderDeferred())); + _jobs.push_back(Job(ResolveDeferred())); + _jobs.push_back(Job(DrawTransparentDeferred())); + _jobs.push_back(Job(ResetGLState())); +} + +RenderDeferredTask::~RenderDeferredTask() { +} + +void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + // sanity checks + assert(sceneContext); + if (!sceneContext->_scene) { + return; + } + + + // Is it possible that we render without a viewFrustum ? + if (!(renderContext->args && renderContext->args->_viewFrustum)) { + return; + } + + renderContext->args->_context->syncCache(); + + for (auto job : _jobs) { + job.run(sceneContext, renderContext); + } +}; + + + +template <> void render::jobRun(const DrawOpaqueDeferred& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + PerformanceTimer perfTimer("DrawOpaqueDeferred"); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + + // render opaques + auto& scene = sceneContext->_scene; + auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape()); + auto& renderDetails = renderContext->args->_details; + + ItemIDsBounds inItems; + inItems.reserve(items.size()); + for (auto id : items) { + inItems.emplace_back(ItemIDAndBounds(id)); + } + ItemIDsBounds& renderedItems = inItems; + + renderContext->_numFeedOpaqueItems = renderedItems.size(); + + ItemIDsBounds culledItems; + culledItems.reserve(inItems.size()); + if (renderContext->_cullOpaque) { + renderDetails.pointTo(RenderDetails::OPAQUE_ITEM); + cullItems(sceneContext, renderContext, renderedItems, culledItems); + renderDetails.pointTo(RenderDetails::OTHER_ITEM); + renderedItems = culledItems; + } + + renderContext->_numDrawnOpaqueItems = renderedItems.size(); + + + ItemIDsBounds sortedItems; + sortedItems.reserve(culledItems.size()); + if (renderContext->_sortOpaque) { + depthSortItems(sceneContext, renderContext, true, renderedItems, sortedItems); // Sort Front to back opaque items! + renderedItems = sortedItems; + } + + // ItemIDsBounds sortedItems; + /* ItemMaterialBucketMap stateSortedItems; + stateSortedItems.allocateStandardMaterialBuckets(); + if (true) { + for (auto& itemIDAndBound : renderedItems) { + stateSortedItems.insert(itemIDAndBound.id, scene->getItem(itemIDAndBound.id).getMaterialKey()); + } + } +*/ + + if (renderContext->_renderOpaque) { + RenderArgs* args = renderContext->args; + gpu::Batch batch; + args->_batch = &batch; + + glm::mat4 projMat; + Transform viewMat; + args->_viewFrustum->evalProjectionMatrix(projMat); + args->_viewFrustum->evalViewTransform(viewMat); + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + + renderContext->args->_renderMode = RenderArgs::NORMAL_RENDER_MODE; + { + GLenum buffers[3]; + int bufferCount = 0; + buffers[bufferCount++] = GL_COLOR_ATTACHMENT0; + buffers[bufferCount++] = GL_COLOR_ATTACHMENT1; + buffers[bufferCount++] = GL_COLOR_ATTACHMENT2; + batch._glDrawBuffers(bufferCount, buffers); + } + + renderItems(sceneContext, renderContext, renderedItems, renderContext->_maxDrawnOpaqueItems); + + args->_context->render((*args->_batch)); + args->_batch = nullptr; + } +} + + +template <> void render::jobRun(const DrawTransparentDeferred& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + PerformanceTimer perfTimer("DrawTransparentDeferred"); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + + // render transparents + auto& scene = sceneContext->_scene; + auto& items = scene->getMasterBucket().at(ItemFilter::Builder::transparentShape()); + auto& renderDetails = renderContext->args->_details; + + ItemIDsBounds inItems; + inItems.reserve(items.size()); + for (auto id : items) { + inItems.push_back(id); + } + ItemIDsBounds& renderedItems = inItems; + + renderContext->_numFeedTransparentItems = renderedItems.size(); + + ItemIDsBounds culledItems; + if (renderContext->_cullTransparent) { + renderDetails.pointTo(RenderDetails::TRANSLUCENT_ITEM); + cullItems(sceneContext, renderContext, inItems, culledItems); + renderDetails.pointTo(RenderDetails::OTHER_ITEM); + renderedItems = culledItems; + } + + renderContext->_numDrawnTransparentItems = renderedItems.size(); + + ItemIDsBounds sortedItems; + if (renderContext->_sortTransparent) { + depthSortItems(sceneContext, renderContext, false, renderedItems, sortedItems); // Sort Back to front transparent items! + renderedItems = sortedItems; + } + + if (renderContext->_renderTransparent) { + RenderArgs* args = renderContext->args; + gpu::Batch batch; + args->_batch = &batch; + + + + + glm::mat4 projMat; + Transform viewMat; + args->_viewFrustum->evalProjectionMatrix(projMat); + args->_viewFrustum->evalViewTransform(viewMat); + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + + args->_renderMode = RenderArgs::NORMAL_RENDER_MODE; + + const float TRANSPARENT_ALPHA_THRESHOLD = 0.0f; + + { + GLenum buffers[3]; + int bufferCount = 0; + buffers[bufferCount++] = GL_COLOR_ATTACHMENT0; + batch._glDrawBuffers(bufferCount, buffers); + args->_alphaThreshold = TRANSPARENT_ALPHA_THRESHOLD; + } + + + renderItems(sceneContext, renderContext, renderedItems, renderContext->_maxDrawnTransparentItems); + + args->_context->render((*args->_batch)); + args->_batch = nullptr; + + // reset blend function to standard... + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); + } +} diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h new file mode 100755 index 0000000000..e2cac53c0d --- /dev/null +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -0,0 +1,66 @@ +// +// RenderDeferredTask.h +// render-utils/src/ +// +// Created by Sam Gateau on 5/29/15. +// Copyright 20154 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_RenderDeferredTask_h +#define hifi_RenderDeferredTask_h + +#include "render/DrawTask.h" + +class PrepareDeferred { +public: +}; +namespace render { +template <> void jobRun(const PrepareDeferred& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); +} + +class RenderDeferred { +public: +}; +namespace render { +template <> void jobRun(const RenderDeferred& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); +} + +class ResolveDeferred { +public: +}; +namespace render { +template <> void jobRun(const ResolveDeferred& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); +} + + +class DrawOpaqueDeferred { +public: +}; +namespace render { +template <> void jobRun(const DrawOpaqueDeferred& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); +} + +class DrawTransparentDeferred { +public: +}; +namespace render { +template <> void jobRun(const DrawTransparentDeferred& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); +} + +class RenderDeferredTask : public render::Task { +public: + + RenderDeferredTask(); + ~RenderDeferredTask(); + + render::Jobs _jobs; + + virtual void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); + +}; + + +#endif // hifi_RenderDeferredTask_h diff --git a/libraries/render-utils/src/RenderUtil.h b/libraries/render-utils/src/RenderUtil.h index cc823dc177..8c1b1e12e7 100644 --- a/libraries/render-utils/src/RenderUtil.h +++ b/libraries/render-utils/src/RenderUtil.h @@ -12,8 +12,6 @@ #ifndef hifi_RenderUtil_h #define hifi_RenderUtil_h -#include - /// Renders a quad from (-1, -1, 0) to (1, 1, 0) with texture coordinates from (sMin, tMin) to (sMax, tMax). void renderFullscreenQuad(float sMin = 0.0f, float sMax = 1.0f, float tMin = 0.0f, float tMax = 1.0f); diff --git a/libraries/render-utils/src/TextRenderer3D.cpp b/libraries/render-utils/src/TextRenderer3D.cpp new file mode 100644 index 0000000000..d081c0480a --- /dev/null +++ b/libraries/render-utils/src/TextRenderer3D.cpp @@ -0,0 +1,498 @@ +// +// TextRenderer3D.cpp +// interface/src/ui +// +// Created by Andrzej Kapolka on 4/24/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 "TextRenderer3D.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include "GLMHelpers.h" +#include "MatrixStack.h" +#include "RenderUtilsLogging.h" + +#include "sdf_text3D_vert.h" +#include "sdf_text3D_frag.h" + +#include "GeometryCache.h" +#include "DeferredLightingEffect.h" + +// FIXME support the shadow effect, or remove it from the API +// FIXME figure out how to improve the anti-aliasing on the +// interior of the outline fonts +const float DEFAULT_POINT_SIZE = 12; + +// Helper functions for reading binary data from an IO device +template +void readStream(QIODevice& in, T& t) { + in.read((char*) &t, sizeof(t)); +} + +template +void readStream(QIODevice& in, T (&t)[N]) { + in.read((char*) t, N); +} + +template +void fillBuffer(QBuffer& buffer, T (&t)[N]) { + buffer.setData((const char*) t, N); +} + +// stores the font metrics for a single character +struct Glyph3D { + QChar c; + glm::vec2 texOffset; + glm::vec2 texSize; + glm::vec2 size; + glm::vec2 offset; + float d; // xadvance - adjusts character positioning + size_t indexOffset; + + // We adjust bounds because offset is the bottom left corner of the font but the top left corner of a QRect + QRectF bounds() const { return glmToRect(offset, size).translated(0.0f, -size.y); } + QRectF textureBounds() const { return glmToRect(texOffset, texSize); } + + void read(QIODevice& in); +}; + +void Glyph3D::read(QIODevice& in) { + uint16_t charcode; + readStream(in, charcode); + c = charcode; + readStream(in, texOffset); + readStream(in, size); + readStream(in, offset); + readStream(in, d); + texSize = size; +} + +struct TextureVertex { + glm::vec2 pos; + glm::vec2 tex; + TextureVertex() {} + TextureVertex(const glm::vec2& pos, const glm::vec2& tex) : pos(pos), tex(tex) {} +}; + +struct QuadBuilder { + TextureVertex vertices[4]; + QuadBuilder(const glm::vec2& min, const glm::vec2& size, + const glm::vec2& texMin, const glm::vec2& texSize) { + // min = bottomLeft + vertices[0] = TextureVertex(min, + texMin + glm::vec2(0.0f, texSize.y)); + vertices[1] = TextureVertex(min + glm::vec2(size.x, 0.0f), + texMin + texSize); + vertices[2] = TextureVertex(min + size, + texMin + glm::vec2(texSize.x, 0.0f)); + vertices[3] = TextureVertex(min + glm::vec2(0.0f, size.y), + texMin); + } + QuadBuilder(const Glyph3D& glyph, const glm::vec2& offset) : + QuadBuilder(offset + glyph.offset - glm::vec2(0.0f, glyph.size.y), glyph.size, + glyph.texOffset, glyph.texSize) {} + +}; + +class Font3D { +public: + Font3D(); + + void read(QIODevice& path); + + glm::vec2 computeExtent(const QString& str) const; + float getRowHeight() const { return _rowHeight; } + + // Render string to batch + void drawString(gpu::Batch& batch, float x, float y, const QString& str, + const glm::vec4& color, TextRenderer3D::EffectType effectType, + const glm::vec2& bound); + +private: + QStringList tokenizeForWrapping(const QString& str) const; + QStringList splitLines(const QString& str) const; + glm::vec2 computeTokenExtent(const QString& str) const; + + const Glyph3D& getGlyph(const QChar& c) const; + + void setupGPU(); + + // maps characters to cached glyph info + // HACK... the operator[] const for QHash returns a + // copy of the value, not a const value reference, so + // we declare the hash as mutable in order to avoid such + // copies + mutable QHash _glyphs; + + // Font characteristics + QString _family; + float _fontSize = 0.0f; + float _rowHeight = 0.0f; + float _leading = 0.0f; + float _ascent = 0.0f; + float _descent = 0.0f; + float _spaceWidth = 0.0f; + + bool _initialized = false; + + // gpu structures + gpu::PipelinePointer _pipeline; + gpu::TexturePointer _texture; + gpu::Stream::FormatPointer _format; + gpu::BufferPointer _verticesBuffer; + gpu::BufferStreamPointer _stream; + unsigned int _numVertices = 0; + + int _fontLoc = -1; + int _outlineLoc = -1; + int _colorLoc = -1; + + // last string render characteristics + QString _lastStringRendered; + glm::vec2 _lastBounds; +}; + +static QHash LOADED_FONTS; + +Font3D* loadFont3D(QIODevice& fontFile) { + Font3D* result = new Font3D(); + result->read(fontFile); + return result; +} + +Font3D* loadFont3D(const QString& family) { + if (!LOADED_FONTS.contains(family)) { + + const QString SDFF_COURIER_PRIME_FILENAME = ":/CourierPrime.sdff"; + const QString SDFF_INCONSOLATA_MEDIUM_FILENAME = ":/InconsolataMedium.sdff"; + const QString SDFF_ROBOTO_FILENAME = ":/Roboto.sdff"; + const QString SDFF_TIMELESS_FILENAME = ":/Timeless.sdff"; + + QString loadFilename; + + if (family == MONO_FONT_FAMILY) { + loadFilename = SDFF_COURIER_PRIME_FILENAME; + } else if (family == INCONSOLATA_FONT_FAMILY) { + loadFilename = SDFF_INCONSOLATA_MEDIUM_FILENAME; + } else if (family == SANS_FONT_FAMILY) { + loadFilename = SDFF_ROBOTO_FILENAME; + } else { + if (!LOADED_FONTS.contains(SERIF_FONT_FAMILY)) { + loadFilename = SDFF_TIMELESS_FILENAME; + } else { + LOADED_FONTS[family] = LOADED_FONTS[SERIF_FONT_FAMILY]; + } + } + + if (!loadFilename.isEmpty()) { + QFile fontFile(loadFilename); + fontFile.open(QIODevice::ReadOnly); + + qCDebug(renderutils) << "Loaded font" << loadFilename << "from Qt Resource System."; + + LOADED_FONTS[family] = loadFont3D(fontFile); + } + } + return LOADED_FONTS[family]; +} + +Font3D::Font3D() { + static bool fontResourceInitComplete = false; + if (!fontResourceInitComplete) { + Q_INIT_RESOURCE(fonts); + fontResourceInitComplete = true; + } +} + +// NERD RAGE: why doesn't QHash have a 'const T & operator[] const' member +const Glyph3D& Font3D::getGlyph(const QChar& c) const { + if (!_glyphs.contains(c)) { + return _glyphs[QChar('?')]; + } + return _glyphs[c]; +} + +QStringList Font3D::splitLines(const QString& str) const { + return str.split('\n'); +} + +QStringList Font3D::tokenizeForWrapping(const QString& str) const { + QStringList tokens; + for(auto line : splitLines(str)) { + if (!tokens.empty()) { + tokens << QString('\n'); + } + tokens << line.split(' '); + } + return tokens; +} + +glm::vec2 Font3D::computeTokenExtent(const QString& token) const { + glm::vec2 advance(0, _fontSize); + foreach(QChar c, token) { + Q_ASSERT(c != '\n'); + advance.x += (c == ' ') ? _spaceWidth : getGlyph(c).d; + } + return advance; +} + +glm::vec2 Font3D::computeExtent(const QString& str) const { + glm::vec2 extent = glm::vec2(0.0f, 0.0f); + + QStringList tokens = splitLines(str); + foreach(const QString& token, tokens) { + glm::vec2 tokenExtent = computeTokenExtent(token); + extent.x = std::max(tokenExtent.x, extent.x); + } + extent.y = tokens.count() * _rowHeight; + + return extent; +} + +void Font3D::read(QIODevice& in) { + uint8_t header[4]; + readStream(in, header); + if (memcmp(header, "SDFF", 4)) { + qFatal("Bad SDFF file"); + } + + uint16_t version; + readStream(in, version); + + // read font name + _family = ""; + if (version > 0x0001) { + char c; + readStream(in, c); + while (c) { + _family += c; + readStream(in, c); + } + } + + // read font data + readStream(in, _leading); + readStream(in, _ascent); + readStream(in, _descent); + readStream(in, _spaceWidth); + _fontSize = _ascent + _descent; + _rowHeight = _fontSize + _leading; + + // Read character count + uint16_t count; + readStream(in, count); + // read metrics data for each character + QVector glyphs(count); + // std::for_each instead of Qt foreach because we need non-const references + std::for_each(glyphs.begin(), glyphs.end(), [&](Glyph3D& g) { + g.read(in); + }); + + // read image data + QImage image; + if (!image.loadFromData(in.readAll(), "PNG")) { + qFatal("Failed to read SDFF image"); + } + + _glyphs.clear(); + glm::vec2 imageSize = toGlm(image.size()); + foreach(Glyph3D g, glyphs) { + // Adjust the pixel texture coordinates into UV coordinates, + g.texSize /= imageSize; + g.texOffset /= imageSize; + // store in the character to glyph hash + _glyphs[g.c] = g; + }; + + image = image.convertToFormat(QImage::Format_RGBA8888); + + gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB); + gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB); + if (image.hasAlphaChannel()) { + formatGPU = gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA); + formatMip = gpu::Element(gpu::VEC4, gpu::UINT8, gpu::BGRA); + } + _texture = gpu::TexturePointer(gpu::Texture::create2D(formatGPU, image.width(), image.height(), + gpu::Sampler(gpu::Sampler::FILTER_MIN_POINT_MAG_LINEAR))); + _texture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); + _texture->autoGenerateMips(-1); +} + +void Font3D::setupGPU() { + if (!_initialized) { + _initialized = true; + + // Setup render pipeline + auto vertexShader = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(sdf_text3D_vert))); + auto pixelShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(sdf_text3D_frag))); + gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vertexShader, pixelShader)); + + gpu::Shader::BindingSet slotBindings; + gpu::Shader::makeProgram(*program, slotBindings); + + _fontLoc = program->getTextures().findLocation("Font"); + _outlineLoc = program->getUniforms().findLocation("Outline"); + _colorLoc = program->getUniforms().findLocation("Color"); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setCullMode(gpu::State::CULL_BACK); + state->setDepthTest(true, true, gpu::LESS_EQUAL); + state->setBlendFunction(false, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + _pipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); + + // Sanity checks + static const int OFFSET = offsetof(TextureVertex, tex); + assert(OFFSET == sizeof(glm::vec2)); + assert(sizeof(glm::vec2) == 2 * sizeof(float)); + assert(sizeof(TextureVertex) == 2 * sizeof(glm::vec2)); + assert(sizeof(QuadBuilder) == 4 * sizeof(TextureVertex)); + + // Setup rendering structures + _format.reset(new gpu::Stream::Format()); + _format->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ), 0); + _format->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), OFFSET); + } +} + +void Font3D::drawString(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color, + TextRenderer3D::EffectType effectType, const glm::vec2& bounds) { + if (str == "") { + return; + } + + if (str != _lastStringRendered || bounds != _lastBounds) { + _verticesBuffer.reset(new gpu::Buffer()); + _numVertices = 0; + _lastStringRendered = str; + _lastBounds = bounds; + + // Top left of text + glm::vec2 advance = glm::vec2(x, y); + foreach(const QString& token, tokenizeForWrapping(str)) { + bool isNewLine = (token == QString('\n')); + bool forceNewLine = false; + + // Handle wrapping + if (!isNewLine && (bounds.x != -1) && (advance.x + computeExtent(token).x > x + bounds.x)) { + // We are out of the x bound, force new line + forceNewLine = true; + } + if (isNewLine || forceNewLine) { + // Character return, move the advance to a new line + advance = glm::vec2(x, advance.y - _rowHeight); + + if (isNewLine) { + // No need to draw anything, go directly to next token + continue; + } else if (computeExtent(token).x > bounds.x) { + // token will never fit, stop drawing + break; + } + } + if ((bounds.y != -1) && (advance.y - _fontSize < -y - bounds.y)) { + // We are out of the y bound, stop drawing + break; + } + + // Draw the token + if (!isNewLine) { + for (auto c : token) { + auto glyph = _glyphs[c]; + + QuadBuilder qd(glyph, advance - glm::vec2(0.0f, _fontSize)); + _verticesBuffer->append(sizeof(QuadBuilder), (const gpu::Byte*)&qd); + _numVertices += 4; + + // Advance by glyph size + advance.x += glyph.d; + } + + // Add space after all non return tokens + advance.x += _spaceWidth; + } + } + } + + setupGPU(); + batch.setPipeline(_pipeline); + batch.setUniformTexture(_fontLoc, _texture); + batch._glUniform1f(_outlineLoc, (effectType == TextRenderer3D::OUTLINE_EFFECT) ? 1.0f : 0.0f); + batch._glUniform4fv(_colorLoc, 1, (const GLfloat*)&color); + + batch.setInputFormat(_format); + batch.setInputBuffer(0, _verticesBuffer, 0, _format->getChannels().at(0)._stride); + batch.draw(gpu::QUADS, _numVertices, 0); +} + +TextRenderer3D* TextRenderer3D::getInstance(const char* family, float pointSize, + int weight, bool italic, EffectType effect, int effectThickness, + const QColor& color) { + if (pointSize < 0) { + pointSize = DEFAULT_POINT_SIZE; + } + return new TextRenderer3D(family, pointSize, weight, italic, effect, + effectThickness, color); +} + +TextRenderer3D::TextRenderer3D(const char* family, float pointSize, int weight, bool italic, + EffectType effect, int effectThickness, const QColor& color) : + _effectType(effect), + _effectThickness(effectThickness), + _pointSize(pointSize), + _color(toGlm(color)), + _font(loadFont3D(family)) { + if (!_font) { + qWarning() << "Unable to load font with family " << family; + _font = loadFont3D("Courier"); + } + if (1 != _effectThickness) { + qWarning() << "Effect thickness not current supported"; + } + if (NO_EFFECT != _effectType && OUTLINE_EFFECT != _effectType) { + qWarning() << "Effect thickness not current supported"; + } +} + +TextRenderer3D::~TextRenderer3D() { +} + +glm::vec2 TextRenderer3D::computeExtent(const QString& str) const { + if (_font) { + return _font->computeExtent(str); + } + return glm::vec2(0.0f, 0.0f); +} + +float TextRenderer3D::getRowHeight() const { + if (_font) { + return _font->getRowHeight(); + } + return 0.0f; +} + +void TextRenderer3D::draw(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color, + const glm::vec2& bounds) { + // The font does all the OpenGL work + if (_font) { + glm::vec4 actualColor(color); + if (actualColor.r < 0) { + actualColor = _color; + } + _font->drawString(batch, x, y, str, actualColor, _effectType, bounds); + } +} + diff --git a/libraries/render-utils/src/TextRenderer3D.h b/libraries/render-utils/src/TextRenderer3D.h new file mode 100644 index 0000000000..8f55d0c977 --- /dev/null +++ b/libraries/render-utils/src/TextRenderer3D.h @@ -0,0 +1,77 @@ +// +// TextRenderer3D.h +// interface/src/ui +// +// Created by Andrzej Kapolka on 4/26/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_TextRenderer3D_h +#define hifi_TextRenderer3D_h + +#include +#include + +// the standard sans serif font family +#define SANS_FONT_FAMILY "Helvetica" + +// the standard sans serif font family +#define SERIF_FONT_FAMILY "Timeless" + +// the standard mono font family +#define MONO_FONT_FAMILY "Courier" + +// the Inconsolata font family +#ifdef Q_OS_WIN +#define INCONSOLATA_FONT_FAMILY "Fixedsys" +#define INCONSOLATA_FONT_WEIGHT QFont::Normal +#else +#define INCONSOLATA_FONT_FAMILY "Inconsolata" +#define INCONSOLATA_FONT_WEIGHT QFont::Bold +#endif + +namespace gpu { +class Batch; +} +class Font3D; + +// TextRenderer3D is actually a fairly thin wrapper around a Font class +// defined in the cpp file. +class TextRenderer3D { +public: + enum EffectType { NO_EFFECT, SHADOW_EFFECT, OUTLINE_EFFECT }; + + static TextRenderer3D* getInstance(const char* family, float pointSize = -1, int weight = -1, bool italic = false, + EffectType effect = NO_EFFECT, int effectThickness = 1, const QColor& color = QColor(255, 255, 255)); + + ~TextRenderer3D(); + + glm::vec2 computeExtent(const QString& str) const; + float getRowHeight() const; + + void draw(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color = glm::vec4(-1.0f), + const glm::vec2& bounds = glm::vec2(-1.0f)); + +private: + TextRenderer3D(const char* family, float pointSize = -1, int weight = -1, bool italic = false, + EffectType effect = NO_EFFECT, int effectThickness = 1, const QColor& color = QColor(255, 255, 255)); + + // the type of effect to apply + const EffectType _effectType; + + // the thickness of the effect + const int _effectThickness; + + const float _pointSize; + + // text color + const glm::vec4 _color; + + Font3D* _font; +}; + + +#endif // hifi_TextRenderer3D_h diff --git a/libraries/render-utils/src/TextureCache.cpp b/libraries/render-utils/src/TextureCache.cpp index c1817169c9..97385cb060 100644 --- a/libraries/render-utils/src/TextureCache.cpp +++ b/libraries/render-utils/src/TextureCache.cpp @@ -9,14 +9,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// include this before QGLWidget, which includes an earlier version of OpenGL +#include +#include #include -#include -#include #include -#include -#include +#include #include #include #include @@ -27,7 +25,6 @@ #include "RenderUtilsLogging.h" #include "TextureCache.h" -#include "gpu/GLBackend.h" #include @@ -247,6 +244,12 @@ GLuint TextureCache::getPrimarySpecularTextureID() { } void TextureCache::setPrimaryDrawBuffers(bool color, bool normal, bool specular) { + gpu::Batch batch; + setPrimaryDrawBuffers(batch, color, normal, specular); + gpu::GLBackend::renderBatch(batch); +} + +void TextureCache::setPrimaryDrawBuffers(gpu::Batch& batch, bool color, bool normal, bool specular) { GLenum buffers[3]; int bufferCount = 0; if (color) { @@ -258,7 +261,7 @@ void TextureCache::setPrimaryDrawBuffers(bool color, bool normal, bool specular) if (specular) { buffers[bufferCount++] = GL_COLOR_ATTACHMENT2; } - glDrawBuffers(bufferCount, buffers); + batch._glDrawBuffers(bufferCount, buffers); } gpu::FramebufferPointer TextureCache::getSecondaryFramebuffer() { @@ -293,7 +296,7 @@ GLuint TextureCache::getShadowDepthTextureID() { } /// Returns a texture version of an image file -gpu::TexturePointer TextureCache::getImageTexture(const QString & path) { +gpu::TexturePointer TextureCache::getImageTexture(const QString& path) { QImage image = QImage(path).mirrored(false, true); gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB); gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB); @@ -380,9 +383,9 @@ ImageReader::ImageReader(const QWeakPointer& texture, TextureType type _content(content) { } -std::once_flag onceListSuppoertedFormatsflag; +std::once_flag onceListSupportedFormatsflag; void listSupportedImageFormats() { - std::call_once(onceListSuppoertedFormatsflag, [](){ + std::call_once(onceListSupportedFormatsflag, [](){ auto supportedFormats = QImageReader::supportedImageFormats(); QString formats; foreach(const QByteArray& f, supportedFormats) { diff --git a/libraries/render-utils/src/TextureCache.h b/libraries/render-utils/src/TextureCache.h index 381359ef80..ba7176b2a4 100644 --- a/libraries/render-utils/src/TextureCache.h +++ b/libraries/render-utils/src/TextureCache.h @@ -20,11 +20,14 @@ #include #include -#include +#include #include #include +namespace gpu { +class Batch; +} class NetworkTexture; typedef QSharedPointer NetworkTexturePointer; @@ -53,7 +56,7 @@ public: const gpu::TexturePointer& getBlueTexture(); /// Returns a texture version of an image file - gpu::TexturePointer getImageTexture(const QString & path); + static gpu::TexturePointer getImageTexture(const QString& path); /// Loads a texture from the specified URL. NetworkTexturePointer getTexture(const QUrl& url, TextureType type = DEFAULT_TEXTURE, bool dilatable = false, @@ -80,6 +83,7 @@ public: /// Enables or disables draw buffers on the primary framebuffer. Note: the primary framebuffer must be bound. void setPrimaryDrawBuffers(bool color, bool normal = false, bool specular = false); + void setPrimaryDrawBuffers(gpu::Batch& batch, bool color, bool normal = false, bool specular = false); /// Returns a pointer to the secondary framebuffer object, used as an additional render target when performing full /// screen effects. diff --git a/libraries/render-utils/src/model.slv b/libraries/render-utils/src/model.slv index 97b5eb640b..2d22c0df51 100755 --- a/libraries/render-utils/src/model.slv +++ b/libraries/render-utils/src/model.slv @@ -19,6 +19,9 @@ const int MAX_TEXCOORDS = 2; uniform mat4 texcoordMatrices[MAX_TEXCOORDS]; +// interpolated eye position +varying vec4 interpolatedPosition; + // the interpolated normal varying vec4 interpolatedNormal; @@ -35,7 +38,7 @@ void main(void) { // standard transform TransformCamera cam = getTransformCamera(); TransformObject obj = getTransformObject(); - <$transformModelToClipPos(cam, obj, gl_Vertex, gl_Position)$> + <$transformModelToEyeAndClipPos(cam, obj, gl_Vertex, interpolatedPosition, gl_Position)$> <$transformModelToEyeDir(cam, obj, gl_Normal, interpolatedNormal.xyz)$> interpolatedNormal = vec4(normalize(interpolatedNormal.xyz), 0.0); diff --git a/libraries/render-utils/src/model_normal_map.slv b/libraries/render-utils/src/model_normal_map.slv index 2f493053dd..58eb03e564 100755 --- a/libraries/render-utils/src/model_normal_map.slv +++ b/libraries/render-utils/src/model_normal_map.slv @@ -23,6 +23,9 @@ uniform mat4 texcoordMatrices[MAX_TEXCOORDS]; // the tangent vector attribute vec3 tangent; +// interpolated eye position +varying vec4 interpolatedPosition; + // the interpolated normal varying vec4 interpolatedNormal; @@ -45,7 +48,7 @@ void main(void) { // standard transform TransformCamera cam = getTransformCamera(); TransformObject obj = getTransformObject(); - <$transformModelToClipPos(cam, obj, gl_Vertex, gl_Position)$> + <$transformModelToEyeAndClipPos(cam, obj, gl_Vertex, interpolatedPosition, gl_Position)$> <$transformModelToEyeDir(cam, obj, gl_Normal, interpolatedNormal.xyz)$> <$transformModelToEyeDir(cam, obj, tangent, interpolatedTangent.xyz)$> diff --git a/libraries/render-utils/src/model_translucent.slf b/libraries/render-utils/src/model_translucent.slf index 9b34951f88..179b7b9e28 100755 --- a/libraries/render-utils/src/model_translucent.slf +++ b/libraries/render-utils/src/model_translucent.slf @@ -11,8 +11,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // - -<@include DeferredBufferWrite.slh@> + <@include model/Material.slh@> @@ -39,4 +38,95 @@ void main(void) { // set the diffuse data // gl_FragData[0] = gl_Color * texture2D(diffuseMap, gl_TexCoord[0].st); +}*/!> +<@include model/Material.slh@> + +// Everything about global lighting + +<@include DeferredLighting.slh@> +<@include gpu/Transform.slh@> +<$declareStandardTransform()$> + + +// Everything about light +<@include model/Light.slh@> + +// The view Matrix +//uniform mat4 invViewMat; + +vec4 evalNormalColor(vec3 dir, float opacity) { + bool isX = (abs(dir.x) > 0.99); + bool isY = (abs(dir.y) > 0.99); + bool isZ = (abs(dir.z) > 0.99); + if (isX || isY || isZ) { + bool negX = (dir.x < -0.995); + bool negY = (dir.y < -0.995); + bool negZ = (dir.z < -0.995); + + if (negX || negY || negZ) { + return vec4(float(isX), float(isY), float(isZ), 0.2); + } else { + return vec4(float(isX), float(isY), float(isZ), 1.0); + } + } + + return vec4(0.5 * dir + vec3(0.5), opacity); +} + +vec4 evalGlobalColor(float shadowAttenuation, vec3 position, vec3 normal, vec3 diffuse, vec3 specular, float gloss, float opacity) { + + // Need the light now + Light light = getLight(); + TransformCamera cam = getTransformCamera(); + vec3 fragNormal; + <$transformEyeToWorldDir(cam, normal, fragNormal)$> + vec3 fragEyeVectorView = normalize(-position); + vec3 fragEyeDir; + <$transformEyeToWorldDir(cam, fragEyeVectorView, fragEyeDir)$> + + vec3 color = opacity * diffuse.rgb * getLightColor(light) * getLightAmbientIntensity(light); + + vec4 shading = evalFragShading(fragNormal, -getLightDirection(light), fragEyeDir, specular, gloss); + + color += vec3(opacity * diffuse + shading.rgb) * shading.w * shadowAttenuation * getLightColor(light) * getLightIntensity(light); + + //return vec4(color, opacity); + return vec4(color, opacity); + //return vec4(diffuse.rgb, opacity); + //return evalNormalColor(fragEyeDir, opacity); +} + +// the diffuse texture +uniform sampler2D diffuseMap; + +// the interpolated view position +varying vec4 interpolatedPosition; + +// the interpolated normal +varying vec4 interpolatedNormal; + +varying vec3 color; + +void main(void) { + vec3 fragPosition = interpolatedPosition.xyz; + + // Fetch diffuse map + vec4 diffuse = texture2D(diffuseMap, gl_TexCoord[0].st); + + Material mat = getMaterial(); + vec3 fragNormal = normalize(interpolatedNormal.xyz); + float fragOpacity = getMaterialOpacity(mat) * diffuse.a; + vec3 fragDiffuse = getMaterialDiffuse(mat) * diffuse.rgb * color; + vec3 fragSpecular = getMaterialSpecular(mat); + float fragGloss = getMaterialShininess(mat); + + vec4 fragColor = evalGlobalColor(1.0, + fragPosition, + fragNormal, + fragDiffuse, + fragSpecular, + fragGloss, + fragOpacity); + + gl_FragColor = fragColor; } diff --git a/libraries/render-utils/src/sdf_text3D.slf b/libraries/render-utils/src/sdf_text3D.slf new file mode 100644 index 0000000000..3980045d08 --- /dev/null +++ b/libraries/render-utils/src/sdf_text3D.slf @@ -0,0 +1,47 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// sdf_text.frag +// fragment shader +// +// Created by Bradley Austin Davis on 2015-02-04 +// Based on fragment shader code from +// https://github.com/paulhoux/Cinder-Samples/blob/master/TextRendering/include/text/Text.cpp +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +uniform sampler2D Font; +uniform float Outline; +uniform vec4 Color; + +const float gamma = 2.6; +const float smoothing = 100.0; +const float interiorCutoff = 0.8; +const float outlineExpansion = 0.2; + +void main() { + // retrieve signed distance + float sdf = texture2D(Font, gl_TexCoord[0].xy).g; + if (Outline == 1.0f) { + if (sdf > interiorCutoff) { + sdf = 1.0 - sdf; + } else { + sdf += outlineExpansion; + } + } + // perform adaptive anti-aliasing of the edges + // The larger we're rendering, the less anti-aliasing we need + float s = smoothing * length(fwidth(gl_TexCoord[0])); + float w = clamp( s, 0.0, 0.5); + float a = smoothstep(0.5 - w, 0.5 + w, sdf); + + // gamma correction for linear attenuation + a = pow(a, 1.0 / gamma); + + if (a < 0.01) { + discard; + } + + // final color + gl_FragColor = vec4(Color.rgb, a); +} \ No newline at end of file diff --git a/libraries/render-utils/src/sdf_text3D.slv b/libraries/render-utils/src/sdf_text3D.slv new file mode 100644 index 0000000000..f7c35a257c --- /dev/null +++ b/libraries/render-utils/src/sdf_text3D.slv @@ -0,0 +1,23 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// sdf_text.vert +// vertex shader +// +// Created by Brad Davis on 10/14/13. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +<@include gpu/Transform.slh@> + +<$declareStandardTransform()$> + +void main() { + gl_TexCoord[0] = gl_MultiTexCoord0; + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToClipPos(cam, obj, gl_Vertex, gl_Position)$> +} \ No newline at end of file diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf index 4e1a09fb92..bbbb44cc51 100644 --- a/libraries/render-utils/src/simple.slf +++ b/libraries/render-utils/src/simple.slf @@ -17,16 +17,7 @@ // the interpolated normal varying vec4 interpolatedNormal; -// the glow intensity -//uniform float glowIntensity; - void main(void) { - /* // set the diffuse, normal, specular data - gl_FragData[0] = vec4(gl_Color.rgb, glowIntensity); - gl_FragData[1] = normalize(normal) * 0.5 + vec4(0.5, 0.5, 0.5, 1.0); - gl_FragData[2] = vec4(gl_FrontMaterial.specular.rgb, gl_FrontMaterial.shininess / 128.0); - */ - packDeferredFragment( normalize(interpolatedNormal.xyz), glowIntensity, diff --git a/libraries/render-utils/src/simple.slv b/libraries/render-utils/src/simple.slv index 9ad47a3e66..380734e70c 100644 --- a/libraries/render-utils/src/simple.slv +++ b/libraries/render-utils/src/simple.slv @@ -12,16 +12,24 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +<@include gpu/Transform.slh@> + +<$declareStandardTransform()$> + // the interpolated normal varying vec4 interpolatedNormal; void main(void) { - // transform and store the normal for interpolation - interpolatedNormal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0)); + gl_TexCoord[0] = gl_MultiTexCoord0; // pass along the diffuse color gl_FrontColor = gl_Color; - // use standard pipeline transform - gl_Position = ftransform(); -} + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToClipPos(cam, obj, gl_Vertex, gl_Position)$> + <$transformModelToEyeDir(cam, obj, gl_Normal, interpolatedNormal.xyz)$> + + interpolatedNormal = vec4(normalize(interpolatedNormal.xyz), 0.0); +} \ No newline at end of file diff --git a/libraries/render-utils/src/simple_textured.slf b/libraries/render-utils/src/simple_textured.slf new file mode 100644 index 0000000000..7444d512e4 --- /dev/null +++ b/libraries/render-utils/src/simple_textured.slf @@ -0,0 +1,32 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// simple.frag +// fragment shader +// +// Created by Clément Brisset on 5/29/15. +// 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 DeferredBufferWrite.slh@> + +// the diffuse texture +uniform sampler2D originalTexture; + +// the interpolated normal +varying vec4 interpolatedNormal; + +void main(void) { + vec4 texel = texture2D(originalTexture, gl_TexCoord[0].st); + + packDeferredFragment( + normalize(interpolatedNormal.xyz), + glowIntensity * texel.a, + gl_Color.rgb * texel.rgb, + gl_FrontMaterial.specular.rgb, + gl_FrontMaterial.shininess); +} \ No newline at end of file diff --git a/libraries/render-utils/src/skin_model.slv b/libraries/render-utils/src/skin_model.slv index c94fc0d151..a13c73c73c 100755 --- a/libraries/render-utils/src/skin_model.slv +++ b/libraries/render-utils/src/skin_model.slv @@ -25,6 +25,9 @@ uniform mat4 texcoordMatrices[MAX_TEXCOORDS]; attribute vec4 clusterIndices; attribute vec4 clusterWeights; +// interpolated eye position +varying vec4 interpolatedPosition; + // the interpolated normal varying vec4 interpolatedNormal; @@ -49,7 +52,7 @@ void main(void) { // standard transform TransformCamera cam = getTransformCamera(); TransformObject obj = getTransformObject(); - <$transformModelToClipPos(cam, obj, position, gl_Position)$> + <$transformModelToEyeAndClipPos(cam, obj, position, interpolatedPosition, gl_Position)$> <$transformModelToEyeDir(cam, obj, interpolatedNormal.xyz, interpolatedNormal.xyz)$> interpolatedNormal = vec4(normalize(interpolatedNormal.xyz), 0.0); diff --git a/libraries/render-utils/src/skin_model_normal_map.slv b/libraries/render-utils/src/skin_model_normal_map.slv index ed552a7aca..3f815ba47e 100755 --- a/libraries/render-utils/src/skin_model_normal_map.slv +++ b/libraries/render-utils/src/skin_model_normal_map.slv @@ -28,6 +28,9 @@ attribute vec3 tangent; attribute vec4 clusterIndices; attribute vec4 clusterWeights; +// interpolated eye position +varying vec4 interpolatedPosition; + // the interpolated normal varying vec4 interpolatedNormal; @@ -37,13 +40,13 @@ varying vec4 interpolatedTangent; varying vec3 color; void main(void) { - vec4 interpolatedPosition = vec4(0.0, 0.0, 0.0, 0.0); + vec4 position = vec4(0.0, 0.0, 0.0, 0.0); interpolatedNormal = vec4(0.0, 0.0, 0.0, 0.0); interpolatedTangent = vec4(0.0, 0.0, 0.0, 0.0); for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[int(clusterIndices[i])]; float clusterWeight = clusterWeights[i]; - interpolatedPosition += clusterMatrix * gl_Vertex * clusterWeight; + position += clusterMatrix * gl_Vertex * clusterWeight; interpolatedNormal += clusterMatrix * vec4(gl_Normal, 0.0) * clusterWeight; interpolatedTangent += clusterMatrix * vec4(tangent, 0.0) * clusterWeight; } @@ -60,7 +63,7 @@ void main(void) { // standard transform TransformCamera cam = getTransformCamera(); TransformObject obj = getTransformObject(); - <$transformModelToClipPos(cam, obj, interpolatedPosition, gl_Position)$> + <$transformModelToEyeAndClipPos(cam, obj, position, interpolatedPosition, gl_Position)$> <$transformModelToEyeDir(cam, obj, interpolatedNormal.xyz, interpolatedNormal.xyz)$> <$transformModelToEyeDir(cam, obj, interpolatedTangent.xyz, interpolatedTangent.xyz)$> diff --git a/libraries/render/CMakeLists.txt b/libraries/render/CMakeLists.txt new file mode 100644 index 0000000000..ee99eb00b9 --- /dev/null +++ b/libraries/render/CMakeLists.txt @@ -0,0 +1,12 @@ +set(TARGET_NAME render) + +AUTOSCRIBE_SHADER_LIB(gpu model) + +# use setup_hifi_library macro to setup our project and link appropriate Qt modules +setup_hifi_library() + +add_dependency_external_projects(glm) +find_package(GLM REQUIRED) +target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) + +link_hifi_libraries(shared gpu model) \ No newline at end of file diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp new file mode 100755 index 0000000000..bfc888ea8a --- /dev/null +++ b/libraries/render/src/render/DrawTask.cpp @@ -0,0 +1,462 @@ +// +// DrawTask.cpp +// render/src/render +// +// Created by Sam Gateau on 5/21/15. +// Copyright 20154 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 +#include + +#include "DrawTask.h" + +#include + +#include "gpu/Batch.h" +#include "gpu/Context.h" + +#include "ViewFrustum.h" +#include "RenderArgs.h" + +using namespace render; + +DrawSceneTask::DrawSceneTask() : Task() { + + _jobs.push_back(Job(DrawOpaque())); + _jobs.push_back(Job(DrawLight())); + _jobs.push_back(Job(DrawTransparent())); + _jobs.push_back(Job(ResetGLState())); +} + +DrawSceneTask::~DrawSceneTask() { +} + +void DrawSceneTask::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + // sanity checks + assert(sceneContext); + if (!sceneContext->_scene) { + return; + } + + + // Is it possible that we render without a viewFrustum ? + if (!(renderContext->args && renderContext->args->_viewFrustum)) { + return; + } + + for (auto job : _jobs) { + job.run(sceneContext, renderContext); + } +}; + +Job::~Job() { +} + +void render::cullItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) { + PerformanceTimer perfTimer("cullItems"); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + + auto& scene = sceneContext->_scene; + RenderArgs* args = renderContext->args; + auto renderDetails = renderContext->args->_details._item; + + renderDetails->_considered += inItems.size(); + + // Culling / LOD + for (auto itemDetails : inItems) { + auto item = scene->getItem(itemDetails.id); + AABox bound; + { + PerformanceTimer perfTimer("getBound"); + + bound = item.getBound(); + } + + if (bound.isNull()) { + outItems.emplace_back(ItemIDAndBounds(itemDetails.id)); // One more Item to render + continue; + } + + // TODO: some entity types (like lights) might want to be rendered even + // when they are outside of the view frustum... + bool outOfView; + { + PerformanceTimer perfTimer("boxInFrustum"); + outOfView = args->_viewFrustum->boxInFrustum(bound) == ViewFrustum::OUTSIDE; + } + if (!outOfView) { + bool bigEnoughToRender; + { + PerformanceTimer perfTimer("shouldRender"); + bigEnoughToRender = (args->_shouldRender) ? args->_shouldRender(args, bound) : true; + } + if (bigEnoughToRender) { + outItems.emplace_back(ItemIDAndBounds(itemDetails.id, bound)); // One more Item to render + } else { + renderDetails->_tooSmall++; + } + } else { + renderDetails->_outOfView++; + } + } + renderDetails->_rendered += outItems.size(); +} + +struct ItemBound { + float _centerDepth = 0.0f; + float _nearDepth = 0.0f; + float _farDepth = 0.0f; + ItemID _id = 0; + + ItemBound() {} + ItemBound(float centerDepth, float nearDepth, float farDepth, ItemID id) : _centerDepth(centerDepth), _nearDepth(nearDepth), _farDepth(farDepth), _id(id) {} +}; + +struct FrontToBackSort { + bool operator() (const ItemBound& left, const ItemBound& right) { + return (left._centerDepth < right._centerDepth); + } +}; + +struct BackToFrontSort { + bool operator() (const ItemBound& left, const ItemBound& right) { + return (left._centerDepth > right._centerDepth); + } +}; + +void render::depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) { + PerformanceTimer perfTimer("depthSortItems"); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + + auto& scene = sceneContext->_scene; + RenderArgs* args = renderContext->args; + + + // Allocate and simply copy + outItems.reserve(inItems.size()); + + + // Make a local dataset of the center distance and closest point distance + std::vector itemBounds; + itemBounds.reserve(outItems.size()); + + for (auto itemDetails : inItems) { + auto item = scene->getItem(itemDetails.id); + auto bound = itemDetails.bounds; // item.getBound(); + float distance = args->_viewFrustum->distanceToCamera(bound.calcCenter()); + + itemBounds.emplace_back(ItemBound(distance, distance, distance, itemDetails.id)); + } + + // sort against Z + if (frontToBack) { + FrontToBackSort frontToBackSort; + std::sort (itemBounds.begin(), itemBounds.end(), frontToBackSort); + } else { + BackToFrontSort backToFrontSort; + std::sort (itemBounds.begin(), itemBounds.end(), backToFrontSort); + } + + // FInally once sorted result to a list of itemID + for (auto& itemBound : itemBounds) { + outItems.emplace_back(itemBound._id); + } +} + +void render::renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, int maxDrawnItems) { + PerformanceTimer perfTimer("renderItems"); + auto& scene = sceneContext->_scene; + RenderArgs* args = renderContext->args; + // render + if ((maxDrawnItems < 0) || (maxDrawnItems > (int) inItems.size())) { + for (auto itemDetails : inItems) { + auto item = scene->getItem(itemDetails.id); + item.render(args); + } + } else { + int numItems = 0; + for (auto itemDetails : inItems) { + auto item = scene->getItem(itemDetails.id); + item.render(args); + numItems++; + if (numItems >= maxDrawnItems) { + return; + } + } + } +} + + +void addClearStateCommands(gpu::Batch& batch) { + batch._glDepthMask(true); + batch._glDepthFunc(GL_LESS); + batch._glDisable(GL_CULL_FACE); + + batch._glActiveTexture(GL_TEXTURE0 + 1); + batch._glBindTexture(GL_TEXTURE_2D, 0); + batch._glActiveTexture(GL_TEXTURE0 + 2); + batch._glBindTexture(GL_TEXTURE_2D, 0); + batch._glActiveTexture(GL_TEXTURE0 + 3); + batch._glBindTexture(GL_TEXTURE_2D, 0); + batch._glActiveTexture(GL_TEXTURE0); + batch._glBindTexture(GL_TEXTURE_2D, 0); + + + // deactivate vertex arrays after drawing + batch._glDisableClientState(GL_NORMAL_ARRAY); + batch._glDisableClientState(GL_VERTEX_ARRAY); + batch._glDisableClientState(GL_TEXTURE_COORD_ARRAY); + batch._glDisableClientState(GL_COLOR_ARRAY); + batch._glDisableVertexAttribArray(gpu::Stream::TANGENT); + batch._glDisableVertexAttribArray(gpu::Stream::SKIN_CLUSTER_INDEX); + batch._glDisableVertexAttribArray(gpu::Stream::SKIN_CLUSTER_WEIGHT); + + // bind with 0 to switch back to normal operation + batch._glBindBuffer(GL_ARRAY_BUFFER, 0); + batch._glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + batch._glBindTexture(GL_TEXTURE_2D, 0); + + // Back to no program + batch._glUseProgram(0); +} + +template <> void render::jobRun(const ResetGLState& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + + gpu::Batch theBatch; + addClearStateCommands(theBatch); + assert(renderContext->args); + renderContext->args->_context->render(theBatch); +} + +template <> void render::jobRun(const DrawOpaque& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + PerformanceTimer perfTimer("DrawOpaque"); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + + // render opaques + auto& scene = sceneContext->_scene; + auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape()); + auto& renderDetails = renderContext->args->_details; + + ItemIDsBounds inItems; + inItems.reserve(items.size()); + for (auto id : items) { + inItems.emplace_back(ItemIDAndBounds(id)); + } + ItemIDsBounds& renderedItems = inItems; + + renderContext->_numFeedOpaqueItems = renderedItems.size(); + + ItemIDsBounds culledItems; + culledItems.reserve(inItems.size()); + if (renderContext->_cullOpaque) { + renderDetails.pointTo(RenderDetails::OPAQUE_ITEM); + cullItems(sceneContext, renderContext, renderedItems, culledItems); + renderDetails.pointTo(RenderDetails::OTHER_ITEM); + renderedItems = culledItems; + } + + renderContext->_numDrawnOpaqueItems = renderedItems.size(); + + + ItemIDsBounds sortedItems; + sortedItems.reserve(culledItems.size()); + if (renderContext->_sortOpaque) { + depthSortItems(sceneContext, renderContext, true, renderedItems, sortedItems); // Sort Front to back opaque items! + renderedItems = sortedItems; + } + + if (renderContext->_renderOpaque) { + RenderArgs* args = renderContext->args; + gpu::Batch batch; + args->_batch = &batch; + + glm::mat4 projMat; + Transform viewMat; + args->_viewFrustum->evalProjectionMatrix(projMat); + args->_viewFrustum->evalViewTransform(viewMat); + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + + renderContext->args->_renderMode = RenderArgs::NORMAL_RENDER_MODE; + { + GLenum buffers[3]; + int bufferCount = 0; + buffers[bufferCount++] = GL_COLOR_ATTACHMENT0; + buffers[bufferCount++] = GL_COLOR_ATTACHMENT1; + buffers[bufferCount++] = GL_COLOR_ATTACHMENT2; + batch._glDrawBuffers(bufferCount, buffers); + } + + renderItems(sceneContext, renderContext, renderedItems, renderContext->_maxDrawnOpaqueItems); + + args->_context->render((*args->_batch)); + args->_batch = nullptr; + } +} + + +template <> void render::jobRun(const DrawTransparent& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + PerformanceTimer perfTimer("DrawTransparent"); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + + // render transparents + auto& scene = sceneContext->_scene; + auto& items = scene->getMasterBucket().at(ItemFilter::Builder::transparentShape()); + auto& renderDetails = renderContext->args->_details; + + ItemIDsBounds inItems; + inItems.reserve(items.size()); + for (auto id : items) { + inItems.emplace_back(id); + } + ItemIDsBounds& renderedItems = inItems; + + renderContext->_numFeedTransparentItems = renderedItems.size(); + + ItemIDsBounds culledItems; + culledItems.reserve(inItems.size()); + if (renderContext->_cullTransparent) { + renderDetails.pointTo(RenderDetails::TRANSLUCENT_ITEM); + cullItems(sceneContext, renderContext, inItems, culledItems); + renderDetails.pointTo(RenderDetails::OTHER_ITEM); + renderedItems = culledItems; + } + + renderContext->_numDrawnTransparentItems = renderedItems.size(); + + ItemIDsBounds sortedItems; + sortedItems.reserve(culledItems.size()); + if (renderContext->_sortTransparent) { + depthSortItems(sceneContext, renderContext, false, renderedItems, sortedItems); // Sort Back to front transparent items! + renderedItems = sortedItems; + } + + if (renderContext->_renderTransparent) { + RenderArgs* args = renderContext->args; + gpu::Batch batch; + args->_batch = &batch; + + glm::mat4 projMat; + Transform viewMat; + args->_viewFrustum->evalProjectionMatrix(projMat); + args->_viewFrustum->evalViewTransform(viewMat); + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + + args->_renderMode = RenderArgs::NORMAL_RENDER_MODE; + + const float MOSTLY_OPAQUE_THRESHOLD = 0.75f; + const float TRANSPARENT_ALPHA_THRESHOLD = 0.0f; + + // render translucent meshes afterwards + { + GLenum buffers[2]; + int bufferCount = 0; + buffers[bufferCount++] = GL_COLOR_ATTACHMENT1; + buffers[bufferCount++] = GL_COLOR_ATTACHMENT2; + batch._glDrawBuffers(bufferCount, buffers); + args->_alphaThreshold = MOSTLY_OPAQUE_THRESHOLD; + } + + renderItems(sceneContext, renderContext, renderedItems, renderContext->_maxDrawnTransparentItems); + + { + GLenum buffers[3]; + int bufferCount = 0; + buffers[bufferCount++] = GL_COLOR_ATTACHMENT0; + batch._glDrawBuffers(bufferCount, buffers); + args->_alphaThreshold = TRANSPARENT_ALPHA_THRESHOLD; + } + + + renderItems(sceneContext, renderContext, renderedItems, renderContext->_maxDrawnTransparentItems); + + args->_context->render((*args->_batch)); + args->_batch = nullptr; + } +} + +template <> void render::jobRun(const DrawLight& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + PerformanceTimer perfTimer("DrawLight"); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + + // render lights + auto& scene = sceneContext->_scene; + auto& items = scene->getMasterBucket().at(ItemFilter::Builder::light()); + + + ItemIDsBounds inItems; + inItems.reserve(items.size()); + for (auto id : items) { + inItems.emplace_back(id); + } + + ItemIDsBounds culledItems; + culledItems.reserve(inItems.size()); + cullItems(sceneContext, renderContext, inItems, culledItems); + + RenderArgs* args = renderContext->args; + gpu::Batch theBatch; + args->_batch = &theBatch; + renderItems(sceneContext, renderContext, culledItems); + args->_context->render((*args->_batch)); + args->_batch = nullptr; +} + +template <> void render::jobRun(const DrawBackground& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + PerformanceTimer perfTimer("DrawBackground"); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + + // render backgrounds + auto& scene = sceneContext->_scene; + auto& items = scene->getMasterBucket().at(ItemFilter::Builder::background()); + + + ItemIDsBounds inItems; + inItems.reserve(items.size()); + for (auto id : items) { + inItems.emplace_back(id); + } + RenderArgs* args = renderContext->args; + gpu::Batch batch; + args->_batch = &batch; + + glm::mat4 projMat; + Transform viewMat; + args->_viewFrustum->evalProjectionMatrix(projMat); + args->_viewFrustum->evalViewTransform(viewMat); + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + + renderItems(sceneContext, renderContext, inItems); + args->_context->render((*args->_batch)); + args->_batch = nullptr; + + // Force the context sync + args->_context->syncCache(); +} + + + +void ItemMaterialBucketMap::insert(const ItemID& id, const model::MaterialKey& key) { + // Insert the itemID in every bucket where it filters true + for (auto& bucket : (*this)) { + if (bucket.first.test(key)) { + bucket.second.push_back(id); + } + } +} + +void ItemMaterialBucketMap::allocateStandardMaterialBuckets() { + (*this)[model::MaterialFilter::Builder::opaqueDiffuse()]; +} diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h new file mode 100755 index 0000000000..1f260583f2 --- /dev/null +++ b/libraries/render/src/render/DrawTask.h @@ -0,0 +1,124 @@ +// +// DrawTask.h +// render/src/render +// +// Created by Sam Gateau on 5/21/15. +// Copyright 20154 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_render_Task_h +#define hifi_render_Task_h + +#include "Engine.h" + +namespace render { + +template void jobRun(const T& jobModel, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { } + +class Job { +public: + + template + Job(T data) : _concept(new Model(data)) {} + Job(const Job& other) : _concept(other._concept) {} + ~Job(); + + virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + if (_concept) { + _concept->run(sceneContext, renderContext); + } + } + +protected: + class Concept { + public: + virtual ~Concept() = default; + virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) = 0; + }; + + template class Model : public Concept { + public: + typedef T Data; + + Data _data; + Model(Data data): _data(data) {} + + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { jobRun(_data, sceneContext, renderContext); } + }; + + std::shared_ptr _concept; +}; + + + + +typedef std::vector Jobs; + +void cullItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outITems); +void depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemIDsBounds& inItems, ItemIDsBounds& outITems); +void renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, int maxDrawnItems = -1); + + + +void materialSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems); + + +class DrawOpaque { +public: +}; +template <> void jobRun(const DrawOpaque& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); + + +class DrawTransparent { +public: +}; +template <> void jobRun(const DrawTransparent& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); + +class DrawLight { +public: +}; +template <> void jobRun(const DrawLight& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); + +class DrawBackground { +public: +}; +template <> void jobRun(const DrawBackground& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); + +class ResetGLState { +public: +}; +template <> void jobRun(const ResetGLState& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); + + +class DrawSceneTask : public Task { +public: + + DrawSceneTask(); + ~DrawSceneTask(); + + Jobs _jobs; + + virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); + +}; + + +// A map of ItemIDs allowing to create bucket lists of SHAPE type items which are filtered by their +// Material +class ItemMaterialBucketMap : public std::map { +public: + + ItemMaterialBucketMap() {} + + void insert(const ItemID& id, const model::MaterialKey& key); + + // standard builders allocating the main buckets + void allocateStandardMaterialBuckets(); +}; + +} + +#endif // hifi_render_Task_h diff --git a/libraries/render/src/render/Engine.cpp b/libraries/render/src/render/Engine.cpp new file mode 100644 index 0000000000..1697271121 --- /dev/null +++ b/libraries/render/src/render/Engine.cpp @@ -0,0 +1,50 @@ +// +// Engine.cpp +// render/src/render +// +// Created by Sam Gateau on 3/3/15. +// 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 "Engine.h" + +#include "DrawTask.h" +using namespace render; + + +Engine::Engine() : + _sceneContext(new SceneContext()), + _renderContext(new RenderContext()) +{ +} + +void Engine::registerScene(const ScenePointer& scene) { + _sceneContext->_scene = scene; +} + +void Engine::setRenderContext(const RenderContext& renderContext) { + (*_renderContext) = renderContext; +} + +void Engine::addTask(const TaskPointer& task) { + if (task) { + _tasks.push_back(task); + } +} + +void Engine::run() { + for (auto task : _tasks) { + task->run(_sceneContext, _renderContext); + } +} + +void Engine::buildStandardTaskPipeline() { + if (!_tasks.empty()) { + _tasks.clear(); + } + + addTask(TaskPointer(new DrawSceneTask())); +} + diff --git a/libraries/render/src/render/Engine.h b/libraries/render/src/render/Engine.h new file mode 100644 index 0000000000..121d6458e9 --- /dev/null +++ b/libraries/render/src/render/Engine.h @@ -0,0 +1,100 @@ +// +// Engine.h +// render/src/render +// +// Created by Sam Gateau on 3/3/15. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_render_Engine_h +#define hifi_render_Engine_h + +#include "Scene.h" + +namespace render { + + +class SceneContext { +public: + ScenePointer _scene; + + SceneContext() {} +}; +typedef std::shared_ptr SceneContextPointer; + + +class RenderContext { +public: + RenderArgs* args; + + bool _cullOpaque = true; + bool _sortOpaque = true; + bool _renderOpaque = true; + bool _cullTransparent = true; + bool _sortTransparent = true; + bool _renderTransparent = true; + + int _numFeedOpaqueItems = 0; + int _numDrawnOpaqueItems = 0; + int _maxDrawnOpaqueItems = -1; + + int _numFeedTransparentItems = 0; + int _numDrawnTransparentItems = 0; + int _maxDrawnTransparentItems = -1; + + RenderContext() {} +}; +typedef std::shared_ptr RenderContextPointer; + +// THe base class for a task that runs on the SceneContext +class Task { +public: + Task() {} + ~Task() {} + + virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {} + +protected: +}; +typedef std::shared_ptr TaskPointer; +typedef std::vector Tasks; + +// The root of the takss, the Engine, should not be known from the Tasks, +// The SceneContext is what navigates from the engine down to the Tasks +class Engine { +public: + + Engine(); + ~Engine() {} + + // Register the scene should be [art of the init phase before running the engine + void registerScene(const ScenePointer& scene); + + // Push a RenderContext + void setRenderContext(const RenderContext& renderContext); + RenderContextPointer getRenderContext() const { return _renderContext; } + + void addTask(const TaskPointer& task); + const Tasks& getTasks() const { return _tasks; } + + + void run(); + + // standard pipeline of tasks + void buildStandardTaskPipeline(); + +protected: + + Tasks _tasks; + + SceneContextPointer _sceneContext; + RenderContextPointer _renderContext; +}; +typedef std::shared_ptr EnginePointer; + +} + +#endif // hifi_render_Engine_h diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp new file mode 100644 index 0000000000..8615f7cf7a --- /dev/null +++ b/libraries/render/src/render/Scene.cpp @@ -0,0 +1,190 @@ +// +// Scene.cpp +// render/src/render +// +// Created by Sam Gateau on 1/11/15. +// 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 "Scene.h" + +using namespace render; + +void ItemBucketMap::insert(const ItemID& id, const ItemKey& key) { + // Insert the itemID in every bucket where it filters true + for (auto& bucket : (*this)) { + if (bucket.first.test(key)) { + bucket.second.insert(id); + } + } +} +void ItemBucketMap::erase(const ItemID& id, const ItemKey& key) { + // Remove the itemID in every bucket where it filters true + for (auto& bucket : (*this)) { + if (bucket.first.test(key)) { + bucket.second.erase(id); + } + } +} + +void ItemBucketMap::reset(const ItemID& id, const ItemKey& oldKey, const ItemKey& newKey) { + // Reset the itemID in every bucket, + // Remove from the buckets where oldKey filters true AND newKey filters false + // Insert into the buckets where newKey filters true + for (auto& bucket : (*this)) { + if (bucket.first.test(oldKey)) { + if (!bucket.first.test(newKey)) { + bucket.second.erase(id); + } + } else if (bucket.first.test(newKey)) { + bucket.second.insert(id); + } + } +} + +void ItemBucketMap::allocateStandardOpaqueTranparentBuckets() { + (*this)[ItemFilter::Builder::opaqueShape()]; + (*this)[ItemFilter::Builder::transparentShape()]; + (*this)[ItemFilter::Builder::light()]; + (*this)[ItemFilter::Builder::background()]; +} + + +void Item::resetPayload(const PayloadPointer& payload) { + if (!payload) { + kill(); + } else { + _payload = payload; + _key = _payload->getKey(); + } +} + +void Item::kill() { + _payload.reset(); + _key._flags.reset(); +} + +void PendingChanges::resetItem(ItemID id, const PayloadPointer& payload) { + _resetItems.push_back(id); + _resetPayloads.push_back(payload); +} + +void PendingChanges::removeItem(ItemID id) { + _removedItems.push_back(id); +} + +void PendingChanges::updateItem(ItemID id, const UpdateFunctorPointer& functor) { + _updatedItems.push_back(id); + _updateFunctors.push_back(functor); +} + + +void PendingChanges::merge(PendingChanges& changes) { + _resetItems.insert(_resetItems.end(), changes._resetItems.begin(), changes._resetItems.end()); + _resetPayloads.insert(_resetPayloads.end(), changes._resetPayloads.begin(), changes._resetPayloads.end()); + _removedItems.insert(_removedItems.end(), changes._removedItems.begin(), changes._removedItems.end()); + _updatedItems.insert(_updatedItems.end(), changes._updatedItems.begin(), changes._updatedItems.end()); + _updateFunctors.insert(_updateFunctors.end(), changes._updateFunctors.begin(), changes._updateFunctors.end()); +} + +Scene::Scene() { + _items.push_back(Item()); // add the itemID #0 to nothing + _masterBucketMap.allocateStandardOpaqueTranparentBuckets(); +} + +ItemID Scene::allocateID() { + // Just increment and return the proevious value initialized at 0 + return _IDAllocator.fetch_add(1); +} + +/// Enqueue change batch to the scene +void Scene::enqueuePendingChanges(const PendingChanges& pendingChanges) { + _changeQueueMutex.lock(); + _changeQueue.push(pendingChanges); + _changeQueueMutex.unlock(); +} + +void consolidateChangeQueue(PendingChangesQueue& queue, PendingChanges& singleBatch) { + while (!queue.empty()) { + auto pendingChanges = queue.front(); + singleBatch.merge(pendingChanges); + queue.pop(); + }; +} + +void Scene::processPendingChangesQueue() { + _changeQueueMutex.lock(); + PendingChanges consolidatedPendingChanges; + consolidateChangeQueue(_changeQueue, consolidatedPendingChanges); + _changeQueueMutex.unlock(); + + _itemsMutex.lock(); + // Here we should be able to check the value of last ItemID allocated + // and allocate new items accordingly + ItemID maxID = _IDAllocator.load(); + if (maxID > _items.size()) { + _items.resize(maxID + 100); // allocate the maxId and more + } + // Now we know for sure that we have enough items in the array to + // capture anything coming from the pendingChanges + resetItems(consolidatedPendingChanges._resetItems, consolidatedPendingChanges._resetPayloads); + removeItems(consolidatedPendingChanges._removedItems); + updateItems(consolidatedPendingChanges._updatedItems, consolidatedPendingChanges._updateFunctors); + + // ready to go back to rendering activities + _itemsMutex.unlock(); +} + +void Scene::resetItems(const ItemIDs& ids, Payloads& payloads) { + auto resetID = ids.begin(); + auto resetPayload = payloads.begin(); + for (;resetID != ids.end(); resetID++, resetPayload++) { + auto& item = _items[(*resetID)]; + auto oldKey = item.getKey(); + item.resetPayload(*resetPayload); + + _masterBucketMap.reset((*resetID), oldKey, item.getKey()); + } + +} + +void Scene::removeItems(const ItemIDs& ids) { + for (auto removedID :ids) { + _masterBucketMap.erase(removedID, _items[removedID].getKey()); + _items[removedID].kill(); + } +} + +void Scene::updateItems(const ItemIDs& ids, UpdateFunctors& functors) { + auto updateID = ids.begin(); + auto updateFunctor = functors.begin(); + for (;updateID != ids.end(); updateID++, updateFunctor++) { + _items[(*updateID)].update((*updateFunctor)); + } +} + +void Scene::registerObserver(ObserverPointer& observer) { + // make sure it's a valid observer + if (observer && (observer->getScene() == nullptr)) { + // Then register the observer + _observers.push_back(observer); + + // And let it do what it wants to do + observer->registerScene(this); + } +} + +void Scene::unregisterObserver(ObserverPointer& observer) { + // make sure it's a valid observer currently registered + if (observer && (observer->getScene() == this)) { + // let it do what it wants to do + observer->unregisterScene(); + + // Then unregister the observer + auto it = std::find(_observers.begin(), _observers.end(), observer); + _observers.erase(it); + } +} + diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h new file mode 100644 index 0000000000..8cb29609ba --- /dev/null +++ b/libraries/render/src/render/Scene.h @@ -0,0 +1,500 @@ +// +// Scene.h +// render/src/render +// +// Created by Sam Gateau on 1/11/15. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_render_Scene_h +#define hifi_render_Scene_h + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "model/Material.h" + +namespace render { + +class Context; + +// Key is the KEY to filter Items and create specialized lists +class ItemKey { +public: + enum FlagBit { + TYPE_SHAPE = 0, // Item is a Shape + TYPE_LIGHT, // Item is a Light + TYPE_BACKGROUND, // Item is a Background + TRANSLUCENT, // Transparent and not opaque, for some odd reason TRANSPARENCY doesn't work... + VIEW_SPACE, // Transformed in view space, and not in world space + DYNAMIC, // Dynamic and bound will change unlike static item + DEFORMED, // Deformed within bound, not solid + INVISIBLE, // Visible or not? could be just here to cast shadow + SHADOW_CASTER, // Item cast shadows + PICKABLE, // Item can be picked/selected + NO_DEPTH_SORT, // Item should not be depth sorted + + NUM_FLAGS, // Not a valid flag + }; + typedef std::bitset Flags; + + // The key is the Flags + Flags _flags; + + ItemKey() : _flags(0) {} + ItemKey(const Flags& flags) : _flags(flags) {} + + class Builder { + Flags _flags{ 0 }; + public: + Builder() {} + + ItemKey build() const { return ItemKey(_flags); } + + Builder& withTypeShape() { _flags.set(TYPE_SHAPE); return (*this); } + Builder& withTypeLight() { _flags.set(TYPE_LIGHT); return (*this); } + Builder& withTypeBackground() { _flags.set(TYPE_BACKGROUND); return (*this); } + Builder& withTransparent() { _flags.set(TRANSLUCENT); return (*this); } + Builder& withViewSpace() { _flags.set(VIEW_SPACE); return (*this); } + Builder& withDynamic() { _flags.set(DYNAMIC); return (*this); } + Builder& withDeformed() { _flags.set(DEFORMED); return (*this); } + Builder& withInvisible() { _flags.set(INVISIBLE); return (*this); } + Builder& withShadowCaster() { _flags.set(SHADOW_CASTER); return (*this); } + Builder& withPickable() { _flags.set(PICKABLE); return (*this); } + Builder& withNoDepthSort() { _flags.set(NO_DEPTH_SORT); return (*this); } + + // Convenient standard keys that we will keep on using all over the place + static ItemKey opaqueShape() { return Builder().withTypeShape().build(); } + static ItemKey transparentShape() { return Builder().withTypeShape().withTransparent().build(); } + static ItemKey light() { return Builder().withTypeLight().build(); } + static ItemKey background() { return Builder().withTypeBackground().build(); } + }; + + bool isOpaque() const { return !_flags[TRANSLUCENT]; } + bool isTransparent() const { return _flags[TRANSLUCENT]; } + + bool isWorldSpace() const { return !_flags[VIEW_SPACE]; } + bool isViewSpace() const { return _flags[VIEW_SPACE]; } + + bool isStatic() const { return !_flags[DYNAMIC]; } + bool isDynamic() const { return _flags[DYNAMIC]; } + + bool isRigid() const { return !_flags[DEFORMED]; } + bool isDeformed() const { return _flags[DEFORMED]; } + + bool isVisible() const { return !_flags[INVISIBLE]; } + bool isInvisible() const { return _flags[INVISIBLE]; } + + bool isShadowCaster() const { return _flags[SHADOW_CASTER]; } + + bool isPickable() const { return _flags[PICKABLE]; } + + bool isDepthSort() const { return !_flags[NO_DEPTH_SORT]; } + bool isNoDepthSort() const { return _flags[NO_DEPTH_SORT]; } +}; + +inline QDebug operator<<(QDebug debug, const ItemKey& itemKey) { + debug << "[ItemKey: isOpaque:" << itemKey.isOpaque() + << ", isStatic:" << itemKey.isStatic() + << ", isWorldSpace:" << itemKey.isWorldSpace() + << "]"; + return debug; +} + +class ItemFilter { +public: + ItemKey::Flags _value{ 0 }; + ItemKey::Flags _mask{ 0 }; + + + ItemFilter(const ItemKey::Flags& value = ItemKey::Flags(0), const ItemKey::Flags& mask = ItemKey::Flags(0)) : _value(value), _mask(mask) {} + + class Builder { + ItemKey::Flags _value{ 0 }; + ItemKey::Flags _mask{ 0 }; + public: + Builder() {} + + ItemFilter build() const { return ItemFilter(_value, _mask); } + + Builder& withTypeShape() { _value.set(ItemKey::TYPE_SHAPE); _mask.set(ItemKey::TYPE_SHAPE); return (*this); } + Builder& withTypeLight() { _value.set(ItemKey::TYPE_LIGHT); _mask.set(ItemKey::TYPE_LIGHT); return (*this); } + Builder& withTypeBackground() { _value.set(ItemKey::TYPE_BACKGROUND); _mask.set(ItemKey::TYPE_BACKGROUND); return (*this); } + + Builder& withOpaque() { _value.reset(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); } + Builder& withTransparent() { _value.set(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); } + + Builder& withWorldSpace() { _value.reset(ItemKey::VIEW_SPACE); _mask.set(ItemKey::VIEW_SPACE); return (*this); } + Builder& withViewSpace() { _value.set(ItemKey::VIEW_SPACE); _mask.set(ItemKey::VIEW_SPACE); return (*this); } + + Builder& withStatic() { _value.reset(ItemKey::DYNAMIC); _mask.set(ItemKey::DYNAMIC); return (*this); } + Builder& withDynamic() { _value.set(ItemKey::DYNAMIC); _mask.set(ItemKey::DYNAMIC); return (*this); } + + Builder& withRigid() { _value.reset(ItemKey::DEFORMED); _mask.set(ItemKey::DEFORMED); return (*this); } + Builder& withDeformed() { _value.set(ItemKey::DEFORMED); _mask.set(ItemKey::DEFORMED); return (*this); } + + Builder& withVisible() { _value.reset(ItemKey::INVISIBLE); _mask.set(ItemKey::INVISIBLE); return (*this); } + Builder& withInvisible() { _value.set(ItemKey::INVISIBLE); _mask.set(ItemKey::INVISIBLE); return (*this); } + + Builder& withNoShadowCaster() { _value.reset(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); } + Builder& withShadowCaster() { _value.set(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); } + + Builder& withPickable() { _value.set(ItemKey::PICKABLE); _mask.set(ItemKey::PICKABLE); return (*this); } + + Builder& withDepthSort() { _value.reset(ItemKey::NO_DEPTH_SORT); _mask.set(ItemKey::NO_DEPTH_SORT); return (*this); } + Builder& withNotDepthSort() { _value.set(ItemKey::NO_DEPTH_SORT); _mask.set(ItemKey::NO_DEPTH_SORT); return (*this); } + + // Convenient standard keys that we will keep on using all over the place + static ItemFilter opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace().build(); } + static ItemFilter transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace().build(); } + static ItemFilter light() { return Builder().withTypeLight().build(); } + static ItemFilter background() { return Builder().withTypeBackground().build(); } + }; + + // Item Filter operator testing if a key pass the filter + bool test(const ItemKey& key) const { return (key._flags & _mask) == (_value & _mask); } + + class Less { + public: + bool operator() (const ItemFilter& left, const ItemFilter& right) const { + if (left._value.to_ulong() == right._value.to_ulong()) { + return left._mask.to_ulong() < right._mask.to_ulong(); + } else { + return left._value.to_ulong() < right._value.to_ulong(); + } + } + }; +}; + +inline QDebug operator<<(QDebug debug, const ItemFilter& me) { + debug << "[ItemFilter: opaqueShape:" << me.test(ItemKey::Builder::opaqueShape()) + << "]"; + return debug; +} + + +class Item { +public: + typedef std::vector Vector; + typedef unsigned int ID; + + static const ID INVALID_ITEM_ID = 0; + + // Bound is the AABBox fully containing this item + typedef AABox Bound; + + // Stats records the life history and performances of this item while performing at rendering and updating. + // This is Used for monitoring and dynamically adjust the quality + class Stats { + public: + int _firstFrame; + }; + + // Update Functor + class UpdateFunctorInterface { + public: + virtual ~UpdateFunctorInterface() {} + }; + typedef std::shared_ptr UpdateFunctorPointer; + + // Payload is whatever is in this Item and implement the Payload Interface + class PayloadInterface { + public: + virtual const ItemKey getKey() const = 0; + virtual const Bound getBound() const = 0; + virtual void render(RenderArgs* args) = 0; + + virtual void update(const UpdateFunctorPointer& functor) = 0; + + virtual const model::MaterialKey getMaterialKey() const = 0; + + ~PayloadInterface() {} + protected: + }; + + + + typedef std::shared_ptr PayloadPointer; + + + + Item() {} + ~Item() {} + + void resetPayload(const PayloadPointer& payload); + void kill(); + + // Check heuristic key + const ItemKey& getKey() const { return _key; } + + // Payload Interface + const Bound getBound() const { return _payload->getBound(); } + void render(RenderArgs* args) { _payload->render(args); } + void update(const UpdateFunctorPointer& updateFunctor) { _payload->update(updateFunctor); } + + // Shape Type Interface + const model::MaterialKey& getMaterialKey() const { return _payload->getMaterialKey(); } + +protected: + PayloadPointer _payload; + ItemKey _key; + + friend class Scene; +}; + + +typedef Item::UpdateFunctorInterface UpdateFunctorInterface; +typedef Item::UpdateFunctorPointer UpdateFunctorPointer; +typedef std::vector UpdateFunctors; + +template class UpdateFunctor : public Item::UpdateFunctorInterface { +public: + typedef std::function Func; + Func _func; + + UpdateFunctor(Func func): _func(func) {} + ~UpdateFunctor() {} +}; + + +inline QDebug operator<<(QDebug debug, const Item& item) { + debug << "[Item: _key:" << item.getKey() << ", bounds:" << item.getBound() << "]"; + return debug; +} + +// THe Payload class is the real Payload to be used +// THis allow anything to be turned into a Payload as long as the required interface functions are available +// When creating a new kind of payload from a new "stuff" class then you need to create specialized version for "stuff" +// of the Payload interface +template const ItemKey payloadGetKey(const std::shared_ptr& payloadData) { return ItemKey(); } +template const Item::Bound payloadGetBound(const std::shared_ptr& payloadData) { return Item::Bound(); } +template void payloadRender(const std::shared_ptr& payloadData, RenderArgs* args) { } + +// Shape type interface +template const model::MaterialKey shapeGetMaterialKey(const std::shared_ptr& payloadData) { return model::MaterialKey(); } + +template class Payload : public Item::PayloadInterface { +public: + typedef std::shared_ptr DataPointer; + typedef UpdateFunctor Updater; + + virtual void update(const UpdateFunctorPointer& functor) { static_cast(functor.get())->_func((*_data)); } + + // Payload general interface + virtual const ItemKey getKey() const { return payloadGetKey(_data); } + virtual const Item::Bound getBound() const { return payloadGetBound(_data); } + virtual void render(RenderArgs* args) { payloadRender(_data, args); } + + // Shape Type interface + virtual const model::MaterialKey getMaterialKey() const { return shapeGetMaterialKey(_data); } + + Payload(const DataPointer& data) : _data(data) {} +protected: + DataPointer _data; +}; + +// Let's show how to make a simple FooPayload example: +/* +class Foo { +public: + mutable ItemKey _myownKey; + void makeMywnKey() const { + _myownKey = ItemKey::Builder().withTypeShape().build(); + } + + const Item::Bound evaluateMyBound() { + // Do stuff here to get your final Bound + return Item::Bound(); + } +}; + +typedef Payload FooPayload; +typedef std::shared_ptr FooPointer; + +// In a Source file, not a header, implement the Payload interface function specialized for Foo: +template <> const ItemKey payloadGetKey(const FooPointer& foo) { + // Foo's way of provinding its Key + foo->makeMyKey(); + return foo->_myownKey; +} +template <> const Item::Bound payloadGetBound(const FooPointer& foo) { + // evaluate Foo's own bound + return foo->evaluateMyBound(); +} + +// In this example, do not specialize the payloadRender call which means the compiler will use the default version which does nothing + +*/ +// End of the example + +typedef Item::PayloadPointer PayloadPointer; +typedef std::vector< PayloadPointer > Payloads; + +// A few typedefs for standard containers of ItemIDs +typedef Item::ID ItemID; +typedef std::vector ItemIDs; +typedef std::set ItemIDSet; + +class ItemIDAndBounds { +public: + ItemIDAndBounds(ItemID id) : id(id) { } + ItemIDAndBounds(ItemID id, const AABox& bounds) : id(id), bounds(bounds) { } + + ItemID id; + AABox bounds; +}; + +typedef std::vector< ItemIDAndBounds > ItemIDsBounds; + +// A map of ItemIDSets allowing to create bucket lists of items which are filtering correctly +class ItemBucketMap : public std::map { +public: + + ItemBucketMap() {} + + void insert(const ItemID& id, const ItemKey& key); + void erase(const ItemID& id, const ItemKey& key); + void reset(const ItemID& id, const ItemKey& oldKey, const ItemKey& newKey); + + // standard builders allocating the main buckets + void allocateStandardOpaqueTranparentBuckets(); + +}; + +class Engine; +class Observer; + + +class PendingChanges { +public: + PendingChanges() {} + ~PendingChanges() {} + + void resetItem(ItemID id, const PayloadPointer& payload); + void removeItem(ItemID id); + + template void updateItem(ItemID id, std::function func) { + updateItem(id, UpdateFunctorPointer(new UpdateFunctor(func))); + } + + void updateItem(ItemID id, const UpdateFunctorPointer& functor); + + void merge(PendingChanges& changes); + + Payloads _resetPayloads; + ItemIDs _resetItems; + ItemIDs _removedItems; + ItemIDs _updatedItems; + UpdateFunctors _updateFunctors; + +protected: +}; +typedef std::queue PendingChangesQueue; + + +// Scene is a container for Items +// Items are introduced, modified or erased in the scene through PendingChanges +// Once per Frame, the PendingChanges are all flushed +// During the flush the standard buckets are updated +// Items are notified accordingly on any update message happening +class Scene { +public: + + class Observer { + public: + Observer(Scene* scene) { + + } + ~Observer() {} + + const Scene* getScene() const { return _scene; } + Scene* editScene() { return _scene; } + + protected: + Scene* _scene = nullptr; + virtual void onRegisterScene() {} + virtual void onUnregisterScene() {} + + friend class Scene; + void registerScene(Scene* scene) { + _scene = scene; + onRegisterScene(); + } + + void unregisterScene() { + onUnregisterScene(); + _scene = 0; + } + }; + typedef std::shared_ptr< Observer > ObserverPointer; + typedef std::vector< ObserverPointer > Observers; + + Scene(); + ~Scene() {} + + /// This call is thread safe, can be called from anywhere to allocate a new ID + ItemID allocateID(); + + /// Enqueue change batch to the scene + void enqueuePendingChanges(const PendingChanges& pendingChanges); + + /// Scene Observer listen to any change and get notified + void registerObserver(ObserverPointer& observer); + void unregisterObserver(ObserverPointer& observer); + + + /// Access the main bucketmap of items + const ItemBucketMap& getMasterBucket() const { return _masterBucketMap; } + + /// Access a particular item form its ID + /// WARNING, There is No check on the validity of the ID, so this could return a bad Item + const Item& getItem(const ItemID& id) const { return _items[id]; } + + unsigned int getNumItems() const { return _items.size(); } + + + void processPendingChangesQueue(); + +protected: + // Thread safe elements that can be accessed from anywhere + std::atomic _IDAllocator{ 1 }; // first valid itemID will be One + std::mutex _changeQueueMutex; + PendingChangesQueue _changeQueue; + + // The actual database + // database of items is protected for editing by a mutex + std::mutex _itemsMutex; + Item::Vector _items; + ItemBucketMap _masterBucketMap; + + void resetItems(const ItemIDs& ids, Payloads& payloads); + void removeItems(const ItemIDs& ids); + void updateItems(const ItemIDs& ids, UpdateFunctors& functors); + + + // The scene context listening for any change to the database + Observers _observers; + + friend class Engine; +}; + + + +typedef std::shared_ptr ScenePointer; +typedef std::vector Scenes; + +} + +#endif // hifi_render_Scene_h diff --git a/libraries/script-engine/src/AudioScriptingInterface.cpp b/libraries/script-engine/src/AudioScriptingInterface.cpp index e210ee6f6e..9e3e924933 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.cpp +++ b/libraries/script-engine/src/AudioScriptingInterface.cpp @@ -28,19 +28,19 @@ AudioScriptingInterface& AudioScriptingInterface::getInstance() { AudioScriptingInterface::AudioScriptingInterface() : _localAudioInterface(NULL) { - + } ScriptAudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions& injectorOptions) { if (QThread::currentThread() != thread()) { ScriptAudioInjector* injector = NULL; - + QMetaObject::invokeMethod(this, "playSound", Qt::BlockingQueuedConnection, Q_RETURN_ARG(ScriptAudioInjector*, injector), Q_ARG(Sound*, sound), Q_ARG(const AudioInjectorOptions&, injectorOptions)); return injector; } - + if (sound) { // stereo option isn't set from script, this comes from sound metadata or filename AudioInjectorOptions optionsCopy = injectorOptions; diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h index bbc9a57db8..d74e1ed1e0 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.h +++ b/libraries/script-engine/src/AudioScriptingInterface.h @@ -22,22 +22,23 @@ class AudioScriptingInterface : public QObject { Q_OBJECT public: static AudioScriptingInterface& getInstance(); - + void setLocalAudioInterface(AbstractAudioInterface* audioInterface) { _localAudioInterface = audioInterface; } - + protected: // this method is protected to stop C++ callers from calling, but invokable from script Q_INVOKABLE ScriptAudioInjector* playSound(Sound* sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions()); - + Q_INVOKABLE void injectGeneratedNoise(bool inject); Q_INVOKABLE void selectPinkNoise(); Q_INVOKABLE void selectSine440(); Q_INVOKABLE void setStereoInput(bool stereo); - + signals: void mutedByMixer(); void environmentMuted(); + void receivedFirstPacket(); private: AudioScriptingInterface(); diff --git a/libraries/script-engine/src/SceneScriptingInterface.cpp b/libraries/script-engine/src/SceneScriptingInterface.cpp index ad76dbcc38..679ec1180f 100644 --- a/libraries/script-engine/src/SceneScriptingInterface.cpp +++ b/libraries/script-engine/src/SceneScriptingInterface.cpp @@ -128,4 +128,35 @@ void SceneScriptingInterface::setShouldRenderEntities(bool shouldRenderEntities) _shouldRenderEntities = shouldRenderEntities; emit shouldRenderEntitiesChanged(_shouldRenderEntities); } -} \ No newline at end of file +} + +void SceneScriptingInterface::setEngineRenderOpaque(bool renderOpaque) { + _engineRenderOpaque = renderOpaque; +} + +void SceneScriptingInterface::setEngineRenderTransparent(bool renderTransparent) { + _engineRenderTransparent = renderTransparent; +} + +void SceneScriptingInterface::setEngineCullOpaque(bool cullOpaque) { + _engineCullOpaque = cullOpaque; +} + +void SceneScriptingInterface::setEngineCullTransparent(bool cullTransparent) { + _engineCullTransparent = cullTransparent; +} + +void SceneScriptingInterface::setEngineSortOpaque(bool sortOpaque) { + _engineSortOpaque = sortOpaque; +} + +void SceneScriptingInterface::setEngineSortTransparent(bool sortTransparent) { + _engineSortOpaque = sortTransparent; +} + +void SceneScriptingInterface::clearEngineCounters() { + _numFeedOpaqueItems = 0; + _numDrawnOpaqueItems = 0; + _numFeedTransparentItems = 0; + _numDrawnTransparentItems = 0; +} diff --git a/libraries/script-engine/src/SceneScriptingInterface.h b/libraries/script-engine/src/SceneScriptingInterface.h index 50aaae83ff..543f55a971 100644 --- a/libraries/script-engine/src/SceneScriptingInterface.h +++ b/libraries/script-engine/src/SceneScriptingInterface.h @@ -67,7 +67,40 @@ public: Q_INVOKABLE void setShouldRenderEntities(bool shouldRenderEntities); Q_INVOKABLE bool shouldRenderEntities() const { return _shouldRenderEntities; } + + + // Controlling the rendering engine + Q_INVOKABLE void setEngineRenderOpaque(bool renderOpaque); + Q_INVOKABLE bool doEngineRenderOpaque() const { return _engineRenderOpaque; } + Q_INVOKABLE void setEngineRenderTransparent(bool renderTransparent); + Q_INVOKABLE bool doEngineRenderTransparent() const { return _engineRenderTransparent; } + Q_INVOKABLE void setEngineCullOpaque(bool cullOpaque); + Q_INVOKABLE bool doEngineCullOpaque() const { return _engineCullOpaque; } + Q_INVOKABLE void setEngineCullTransparent(bool cullTransparent); + Q_INVOKABLE bool doEngineCullTransparent() const { return _engineCullTransparent; } + + Q_INVOKABLE void setEngineSortOpaque(bool sortOpaque); + Q_INVOKABLE bool doEngineSortOpaque() const { return _engineSortOpaque; } + Q_INVOKABLE void setEngineSortTransparent(bool sortTransparent); + Q_INVOKABLE bool doEngineSortTransparent() const { return _engineSortTransparent; } + + void clearEngineCounters(); + void setEngineDrawnOpaqueItems(int count) { _numDrawnOpaqueItems = count; } + Q_INVOKABLE int getEngineNumDrawnOpaqueItems() { return _numDrawnOpaqueItems; } + void setEngineDrawnTransparentItems(int count) { _numDrawnTransparentItems = count; } + Q_INVOKABLE int getEngineNumDrawnTransparentItems() { return _numDrawnTransparentItems; } + + void setEngineFeedOpaqueItems(int count) { _numFeedOpaqueItems = count; } + Q_INVOKABLE int getEngineNumFeedOpaqueItems() { return _numFeedOpaqueItems; } + void setEngineFeedTransparentItems(int count) { _numFeedTransparentItems = count; } + Q_INVOKABLE int getEngineNumFeedTransparentItems() { return _numFeedTransparentItems; } + + Q_INVOKABLE void setEngineMaxDrawnOpaqueItems(int count) { _maxDrawnOpaqueItems = count; } + Q_INVOKABLE int getEngineMaxDrawnOpaqueItems() { return _maxDrawnOpaqueItems; } + Q_INVOKABLE void setEngineMaxDrawnTransparentItems(int count) { _maxDrawnTransparentItems = count; } + Q_INVOKABLE int getEngineMaxDrawnTransparentItems() { return _maxDrawnTransparentItems; } + signals: void shouldRenderAvatarsChanged(bool shouldRenderAvatars); void shouldRenderEntitiesChanged(bool shouldRenderEntities); @@ -79,6 +112,22 @@ protected: bool _shouldRenderAvatars = true; bool _shouldRenderEntities = true; + + bool _engineRenderOpaque = true; + bool _engineRenderTransparent = true; + bool _engineCullOpaque = true; + bool _engineCullTransparent = true; + bool _engineSortOpaque = true; + bool _engineSortTransparent = true; + + int _numFeedOpaqueItems = 0; + int _numDrawnOpaqueItems = 0; + int _numFeedTransparentItems = 0; + int _numDrawnTransparentItems = 0; + + int _maxDrawnOpaqueItems = -1; + int _maxDrawnTransparentItems = -1; + }; #endif // hifi_SceneScriptingInterface_h diff --git a/libraries/shared/src/AABox.cpp b/libraries/shared/src/AABox.cpp index 3c6be43850..37fba37adc 100644 --- a/libraries/shared/src/AABox.cpp +++ b/libraries/shared/src/AABox.cpp @@ -42,12 +42,6 @@ glm::vec3 AABox::calcCenter() const { return center; } -glm::vec3 AABox::calcTopFarLeft() const { - glm::vec3 topFarLeft(_corner); - topFarLeft += _scale; - return topFarLeft; -}; - void AABox::scale(float scale) { _corner = _corner * scale; _scale = _scale * scale; diff --git a/libraries/shared/src/AABox.h b/libraries/shared/src/AABox.h index 9beb0a85f2..f185122534 100644 --- a/libraries/shared/src/AABox.h +++ b/libraries/shared/src/AABox.h @@ -47,7 +47,11 @@ public: float getLargestDimension() const { return glm::max(_scale.x, glm::max(_scale.y, _scale.z)); } glm::vec3 calcCenter() const; - glm::vec3 calcTopFarLeft() const; + glm::vec3 calcTopFarLeft() const { return _corner + _scale; } + + const glm::vec3& getMinimum() const { return _corner; } + glm::vec3 getMaximum() const { return _corner + _scale; } + glm::vec3 getVertex(BoxVertex vertex) const; const glm::vec3& getMinimumPoint() const { return _corner; } diff --git a/libraries/shared/src/Extents.h b/libraries/shared/src/Extents.h index 489d48b1c1..647f33699a 100644 --- a/libraries/shared/src/Extents.h +++ b/libraries/shared/src/Extents.h @@ -23,6 +23,10 @@ class AABox; class Extents { public: + Extents(const glm::vec3& minimum, const glm::vec3& maximum) : minimum(minimum), maximum(maximum) { } + Extents() { reset(); } + Extents(const AABox& box) { reset(); add(box); } + /// set minimum and maximum to FLT_MAX and -FLT_MAX respectively void reset(); @@ -58,7 +62,7 @@ public: /// \return new Extents which is original rotated around orign by rotation Extents getRotated(const glm::quat& rotation) const { - Extents temp = { minimum, maximum }; + Extents temp(minimum, maximum); temp.rotate(rotation); return temp; } diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index 84c8ae4939..79e83e9b40 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -18,7 +18,7 @@ #include "PathUtils.h" -QString& PathUtils::resourcesPath() { +const QString& PathUtils::resourcesPath() { #ifdef Q_OS_MAC static QString staticResourcePath = QCoreApplication::applicationDirPath() + "/../Resources/"; #else diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h index 6b6893574b..12b1d57641 100644 --- a/libraries/shared/src/PathUtils.h +++ b/libraries/shared/src/PathUtils.h @@ -12,12 +12,17 @@ #ifndef hifi_PathUtils_h #define hifi_PathUtils_h +#include -#include +#include "DependencyManager.h" -namespace PathUtils { - QString& resourcesPath(); -} +class PathUtils : public QObject, public Dependency { + Q_OBJECT + SINGLETON_DEPENDENCY + Q_PROPERTY(QString resources READ resourcesPath) +public: + static const QString& resourcesPath(); +}; QString fileNameWithoutExtension(const QString& fileName, const QVector possibleExtensions); QString findMostRecentFileExtension(const QString& originalFileName, QVector possibleExtensions); diff --git a/libraries/shared/src/RenderArgs.h b/libraries/shared/src/RenderArgs.h index 3019535d2a..84b3a202b4 100644 --- a/libraries/shared/src/RenderArgs.h +++ b/libraries/shared/src/RenderArgs.h @@ -12,11 +12,60 @@ #ifndef hifi_RenderArgs_h #define hifi_RenderArgs_h -class ViewFrustum; +#include + +class AABox; class OctreeRenderer; +class ViewFrustum; +namespace gpu { +class Batch; +class Context; +} + +class RenderDetails { +public: + enum Type { + OPAQUE_ITEM, + TRANSLUCENT_ITEM, + OTHER_ITEM + }; + + struct Item { + int _considered = 0; + int _rendered = 0; + int _outOfView = 0; + int _tooSmall = 0; + }; + + int _materialSwitches = 0; + int _trianglesRendered = 0; + int _quadsRendered = 0; + + Item _opaque; + Item _translucent; + Item _other; + + Item* _item = &_other; + + void pointTo(Type type) { + switch (type) { + case OPAQUE_ITEM: + _item = &_opaque; + break; + case TRANSLUCENT_ITEM: + _item = &_translucent; + break; + case OTHER_ITEM: + _item = &_other; + break; + } + } +}; class RenderArgs { public: + typedef std::function ShoudRenderFunctor; + enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE, MIRROR_RENDER_MODE }; enum RenderSide { MONO, STEREO_LEFT, STEREO_RIGHT }; @@ -27,30 +76,17 @@ public: RENDER_DEBUG_SIMULATION_OWNERSHIP = 2, }; - RenderArgs(OctreeRenderer* renderer = nullptr, + RenderArgs(gpu::Context* context = nullptr, + OctreeRenderer* renderer = nullptr, ViewFrustum* viewFrustum = nullptr, float sizeScale = 1.0f, int boundaryLevelAdjust = 0, RenderMode renderMode = DEFAULT_RENDER_MODE, RenderSide renderSide = MONO, DebugFlags debugFlags = RENDER_DEBUG_NONE, - - int elementsTouched = 0, - int itemsRendered = 0, - int itemsOutOfView = 0, - int itemsTooSmall = 0, - - int meshesConsidered = 0, - int meshesRendered = 0, - int meshesOutOfView = 0, - int meshesTooSmall = 0, - - int materialSwitches = 0, - int trianglesRendered = 0, - int quadsRendered = 0, - - int translucentMeshPartsRendered = 0, - int opaqueMeshPartsRendered = 0) : + gpu::Batch* batch = nullptr, + ShoudRenderFunctor shouldRender = nullptr) : + _context(context), _renderer(renderer), _viewFrustum(viewFrustum), _sizeScale(sizeScale), @@ -58,49 +94,24 @@ public: _renderMode(renderMode), _renderSide(renderSide), _debugFlags(debugFlags), - - _elementsTouched(elementsTouched), - _itemsRendered(itemsRendered), - _itemsOutOfView(itemsOutOfView), - _itemsTooSmall(itemsTooSmall), - - _meshesConsidered(meshesConsidered), - _meshesRendered(meshesRendered), - _meshesOutOfView(meshesOutOfView), - _meshesTooSmall(meshesTooSmall), - - _materialSwitches(materialSwitches), - _trianglesRendered(trianglesRendered), - _quadsRendered(quadsRendered), - - _translucentMeshPartsRendered(translucentMeshPartsRendered), - _opaqueMeshPartsRendered(opaqueMeshPartsRendered) { + _batch(batch), + _shouldRender(shouldRender) { } - OctreeRenderer* _renderer; - ViewFrustum* _viewFrustum; - float _sizeScale; - int _boundaryLevelAdjust; - RenderMode _renderMode; - RenderSide _renderSide; - DebugFlags _debugFlags; + gpu::Context* _context = nullptr; + OctreeRenderer* _renderer = nullptr; + ViewFrustum* _viewFrustum = nullptr; + float _sizeScale = 1.0f; + int _boundaryLevelAdjust = 0; + RenderMode _renderMode = DEFAULT_RENDER_MODE; + RenderSide _renderSide = MONO; + DebugFlags _debugFlags = RENDER_DEBUG_NONE; + gpu::Batch* _batch = nullptr; + ShoudRenderFunctor _shouldRender; + + RenderDetails _details; - int _elementsTouched; - int _itemsRendered; - int _itemsOutOfView; - int _itemsTooSmall; - - int _meshesConsidered; - int _meshesRendered; - int _meshesOutOfView; - int _meshesTooSmall; - - int _materialSwitches; - int _trianglesRendered; - int _quadsRendered; - - int _translucentMeshPartsRendered; - int _opaqueMeshPartsRendered; + float _alphaThreshold = 0.5f; }; #endif // hifi_RenderArgs_h diff --git a/libraries/shared/src/Transform.h b/libraries/shared/src/Transform.h index ec4513e51c..f3b5b14385 100644 --- a/libraries/shared/src/Transform.h +++ b/libraries/shared/src/Transform.h @@ -22,6 +22,18 @@ #include +inline bool isValidScale(glm::vec3 scale) { + bool result = scale.x != 0.0f && scale.y != 0.0f && scale.z != 0.0f; + assert(result); + return result; +} + +inline bool isValidScale(float scale) { + bool result = scale != 0.0f; + assert(result); + return result; +} + class Transform { public: typedef glm::mat4 Mat4; @@ -32,12 +44,22 @@ public: typedef glm::quat Quat; Transform() : - _rotation(1.0f, 0, 0, 0), + _rotation(1.0f, 0.0f, 0.0f, 0.0f), _scale(1.0f), - _translation(0), + _translation(0.0f), _flags(FLAG_CACHE_INVALID_BITSET) // invalid cache { } + Transform(Quat rotation, Vec3 scale, Vec3 translation) : + _rotation(rotation), + _scale(scale), + _translation(translation), + _flags(FLAG_CACHE_INVALID_BITSET) // invalid cache + { + if (!isValidScale(_scale)) { + _scale = Vec3(1.0f); + } + } Transform(const Transform& transform) : _rotation(transform._rotation), _scale(transform._scale), @@ -159,8 +181,8 @@ protected: }; inline void Transform::setIdentity() { - _translation = Vec3(0); - _rotation = Quat(1.0f, 0, 0, 0); + _translation = Vec3(0.0f); + _rotation = Quat(1.0f, 0.0f, 0.0f, 0.0f); _scale = Vec3(1.0f); _flags = Flags(FLAG_CACHE_INVALID_BITSET); } @@ -180,19 +202,25 @@ inline void Transform::setTranslation(const Vec3& translation) { } inline void Transform::preTranslate(const Vec3& translation) { - if (translation == Vec3() ) return; + if (translation == Vec3()) { + return; + } invalidCache(); flagTranslation(); _translation += translation; } inline void Transform::postTranslate(const Vec3& translation) { - if (translation == Vec3() ) return; + if (translation == Vec3()) { + return; + } invalidCache(); flagTranslation(); Vec3 scaledT = translation; - if (isScaling()) scaledT *= _scale; + if (isScaling()) { + scaledT *= _scale; + } if (isRotating()) { _translation += glm::rotate(_rotation, scaledT); @@ -216,7 +244,9 @@ inline void Transform::setRotation(const Quat& rotation) { } inline void Transform::preRotate(const Quat& rotation) { - if (rotation == Quat()) return; + if (rotation == Quat()) { + return; + } invalidCache(); if (isRotating()) { _rotation = rotation * _rotation; @@ -229,7 +259,9 @@ inline void Transform::preRotate(const Quat& rotation) { } inline void Transform::postRotate(const Quat& rotation) { - if (rotation == Quat()) return; + if (rotation == Quat()) { + return; + } invalidCache(); if (isNonUniform()) { @@ -262,8 +294,12 @@ inline const Transform::Vec3& Transform::getScale() const { } inline void Transform::setScale(float scale) { + if (!isValidScale(scale)) { + return; + } invalidCache(); flagUniform(); + if (scale == 1.0f) { unflagScaling(); } else { @@ -273,6 +309,9 @@ inline void Transform::setScale(float scale) { } inline void Transform::setScale(const Vec3& scale) { + if (!isValidScale(scale)) { + return; + } if ((scale.x == scale.y) && (scale.x == scale.z)) { setScale(scale.x); } else { @@ -284,9 +323,11 @@ inline void Transform::setScale(const Vec3& scale) { } inline void Transform::postScale(float scale) { - if (scale == 1.0f) return; + if (isValidScale(scale) || scale == 1.0f) { + return; + } if (isScaling()) { - // if already scaling, just invalid cache and aply uniform scale + // if already scaling, just invalid cache and apply uniform scale invalidCache(); _scale *= scale; } else { @@ -295,6 +336,9 @@ inline void Transform::postScale(float scale) { } inline void Transform::postScale(const Vec3& scale) { + if (!isValidScale(scale)) { + return; + } invalidCache(); if (isScaling()) { _scale *= scale; @@ -353,7 +397,7 @@ inline Transform::Mat4& Transform::getRotationScaleMatrixInverse(Mat4& result) c inline void Transform::evalFromRawMatrix(const Mat4& matrix) { // for now works only in the case of TRS transformation - if ((matrix[0][3] == 0) && (matrix[1][3] == 0) && (matrix[2][3] == 0) && (matrix[3][3] == 1.0f)) { + if ((matrix[0][3] == 0.0f) && (matrix[1][3] == 0.0f) && (matrix[2][3] == 0.0f) && (matrix[3][3] == 1.0f)) { setTranslation(Vec3(matrix[3])); evalFromRawMatrix(Mat3(matrix)); } @@ -370,15 +414,10 @@ inline void Transform::evalFromRawMatrix(const Mat3& rotationScaleMatrix) { inline Transform& Transform::evalInverse(Transform& inverse) const { inverse.setIdentity(); if (isScaling()) { - // TODO: At some point we will face the case when scale is 0 and so 1/0 will blow up... - // WHat should we do for this one? - assert(_scale.x != 0); - assert(_scale.y != 0); - assert(_scale.z != 0); if (isNonUniform()) { - inverse.setScale(Vec3(1.0f/_scale.x, 1.0f/_scale.y, 1.0f/_scale.z)); + inverse.setScale(Vec3(1.0f) / _scale); } else { - inverse.setScale(1.0f/_scale.x); + inverse.setScale(1.0f / _scale.x); } } if (isRotating()) { @@ -414,8 +453,7 @@ inline Transform& Transform::inverseMult( Transform& result, const Transform& le result.setIdentity(); if (left.isScaling()) { - const Vec3& s = left.getScale(); - result.setScale(Vec3(1.0f / s.x, 1.0f / s.y, 1.0f / s.z)); + result.setScale(Vec3(1.0f) / left.getScale()); } if (left.isRotating()) { result.postRotate(glm::conjugate(left.getRotation())); diff --git a/tests/render-utils/src/main.cpp b/tests/render-utils/src/main.cpp index 0ba7416b28..87338e414b 100644 --- a/tests/render-utils/src/main.cpp +++ b/tests/render-utils/src/main.cpp @@ -9,7 +9,6 @@ // #include "TextRenderer.h" -#include "MatrixStack.h" #include #include