diff --git a/examples/entityScripts/lightController.js b/examples/entityScripts/lightController.js index 31e07c4602..6d6c0a59bd 100644 --- a/examples/entityScripts/lightController.js +++ b/examples/entityScripts/lightController.js @@ -212,12 +212,8 @@ this.preload = function(entityID) { this.preOperation(entityID); - }; - this.unload = function(){ - Entities.deleteEntity(this.lightID); } - this.clickReleaseOnEntity = function(entityID, mouseEvent) { this.preOperation(entityID); diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index 0c2e09ef16..a1f1a5ea79 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -330,6 +330,8 @@ elLightSections[i].style.display = 'block'; } + elLightSpotLight.checked = properties.isSpotlight; + elLightColorRed.value = properties.color.red; elLightColorGreen.value = properties.color.green; elLightColorBlue.value = properties.color.blue; diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index b6c536f063..70af987243 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -301,6 +301,8 @@ SelectionDisplay = (function () { var grabberSolid = true; var grabberMoveUpPosition = { x: 0, y: 0, z: 0 }; + var lightOverlayColor = { red: 255, green: 153, blue: 0 }; + var grabberPropertiesCorner = { position: { x:0, y: 0, z: 0}, size: grabberSizeCorner, @@ -340,6 +342,11 @@ SelectionDisplay = (function () { borderSize: 1.4, }; + var spotLightLineProperties = { + color: lightOverlayColor, + lineWidth: 1.5, + }; + var highlightBox = Overlays.addOverlay("cube", { position: { x:0, y: 0, z: 0}, size: 1, @@ -434,6 +441,44 @@ SelectionDisplay = (function () { var grabberEdgeFR = Overlays.addOverlay("cube", grabberPropertiesEdge); var grabberEdgeFL = Overlays.addOverlay("cube", grabberPropertiesEdge); + var grabberSpotLightCircle = Overlays.addOverlay("circle3d", { + color: lightOverlayColor, + isSolid: false + }); + var grabberSpotLightLineT = Overlays.addOverlay("line3d", spotLightLineProperties); + var grabberSpotLightLineB = Overlays.addOverlay("line3d", spotLightLineProperties); + var grabberSpotLightLineL = Overlays.addOverlay("line3d", spotLightLineProperties); + var grabberSpotLightLineR = Overlays.addOverlay("line3d", spotLightLineProperties); + + var grabberSpotLightCenter = Overlays.addOverlay("cube", grabberPropertiesEdge); + var grabberSpotLightRadius = Overlays.addOverlay("cube", grabberPropertiesEdge); + var grabberSpotLightL = Overlays.addOverlay("cube", grabberPropertiesEdge); + var grabberSpotLightR = Overlays.addOverlay("cube", grabberPropertiesEdge); + var grabberSpotLightT = Overlays.addOverlay("cube", grabberPropertiesEdge); + var grabberSpotLightB = Overlays.addOverlay("cube", grabberPropertiesEdge); + + var grabberPointLightCircleX = Overlays.addOverlay("circle3d", { + rotation: Quat.fromPitchYawRollDegrees(0, 90, 0), + color: lightOverlayColor, + isSolid: false + }); + var grabberPointLightCircleY = Overlays.addOverlay("circle3d", { + rotation: Quat.fromPitchYawRollDegrees(90, 0, 0), + color: lightOverlayColor, + isSolid: false + }); + var grabberPointLightCircleZ = Overlays.addOverlay("circle3d", { + rotation: Quat.fromPitchYawRollDegrees(0, 0, 0), + color: lightOverlayColor, + isSolid: false + }); + var grabberPointLightT = Overlays.addOverlay("cube", grabberPropertiesEdge); + var grabberPointLightB = Overlays.addOverlay("cube", grabberPropertiesEdge); + var grabberPointLightL = Overlays.addOverlay("cube", grabberPropertiesEdge); + var grabberPointLightR = Overlays.addOverlay("cube", grabberPropertiesEdge); + var grabberPointLightF = Overlays.addOverlay("cube", grabberPropertiesEdge); + var grabberPointLightN = Overlays.addOverlay("cube", grabberPropertiesEdge); + var stretchHandles = [ grabberLBN, grabberRBN, @@ -461,6 +506,25 @@ SelectionDisplay = (function () { grabberEdgeNL, grabberEdgeFR, grabberEdgeFL, + + grabberSpotLightLineT, + grabberSpotLightLineB, + grabberSpotLightLineL, + grabberSpotLightLineR, + + grabberSpotLightCenter, + grabberSpotLightRadius, + grabberSpotLightL, + grabberSpotLightR, + grabberSpotLightT, + grabberSpotLightB, + + grabberPointLightT, + grabberPointLightB, + grabberPointLightL, + grabberPointLightR, + grabberPointLightF, + grabberPointLightN, ]; @@ -648,6 +712,10 @@ SelectionDisplay = (function () { yRailOverlay, zRailOverlay, baseOfEntityProjectionOverlay, + grabberSpotLightCircle, + grabberPointLightCircleX, + grabberPointLightCircleY, + grabberPointLightCircleZ, ].concat(stretchHandles); overlayNames[highlightBox] = "highlightBox"; @@ -942,13 +1010,20 @@ SelectionDisplay = (function () { var translateHandlesVisible = true; var stretchHandlesVisible = true; var selectionBoxVisible = true; + var isPointLight = false; + + if (selectionManager.selections.length == 1) { + var properties = Entities.getEntityProperties(selectionManager.selections[0]); + isPointLight = properties.type == "Light" && !properties.isSpotlight; + } + if (mode == "ROTATE_YAW" || mode == "ROTATE_PITCH" || mode == "ROTATE_ROLL" || mode == "TRANSLATE_X in case they Z") { rotationOverlaysVisible = true; rotateHandlesVisible = false; translateHandlesVisible = false; stretchHandlesVisible = false; selectionBoxVisible = false; - } else if (mode == "TRANSLATE_UP_DOWN") { + } else if (mode == "TRANSLATE_UP_DOWN" || isPointLight) { rotateHandlesVisible = false; stretchHandlesVisible = false; } else if (mode != "UNKNOWN") { @@ -1122,6 +1197,171 @@ SelectionDisplay = (function () { var stretchHandlesVisible = spaceMode == SPACE_LOCAL; var extendedStretchHandlesVisible = stretchHandlesVisible && showExtendedStretchHandles; + + if (selectionManager.selections.length == 1 ) { + var properties = Entities.getEntityProperties(selectionManager.selections[0]); + if (properties.type == "Light" && properties.isSpotlight == true) { + var stretchHandlesVisible = false; + var extendedStretchHandlesVisible = false; + + Overlays.editOverlay(grabberSpotLightCenter, { + position: position, + visible: false, + }); + Overlays.editOverlay(grabberSpotLightRadius, { + position: NEAR, + rotation: rotation, + visible: true, + }); + var distance = (properties.dimensions.z / 2) * Math.tan(properties.cutoff * (Math.PI / 180)); + + Overlays.editOverlay(grabberSpotLightL, { + position: EdgeNL, + rotation: rotation, + visible: true, + }); + Overlays.editOverlay(grabberSpotLightR, { + position: EdgeNR, + rotation: rotation, + visible: true, + }); + Overlays.editOverlay(grabberSpotLightT, { + position: EdgeTN, + rotation: rotation, + visible: true, + }); + Overlays.editOverlay(grabberSpotLightB, { + position: EdgeBN, + rotation: rotation, + visible: true, + }); + Overlays.editOverlay(grabberSpotLightCircle, { + position: NEAR, + dimensions: { x: distance * 2, y: distance * 2, z: 1 }, + lineWidth: 1.5, + rotation: rotation, + visible: true, + }); + + Overlays.editOverlay(grabberSpotLightLineT, { + start: position, + end: EdgeTN, + visible: true, + }); + Overlays.editOverlay(grabberSpotLightLineB, { + start: position, + end: EdgeBN, + visible: true, + }); + Overlays.editOverlay(grabberSpotLightLineR, { + start: position, + end: EdgeNR, + visible: true, + }); + Overlays.editOverlay(grabberSpotLightLineL, { + start: position, + end: EdgeNL, + visible: true, + }); + + Overlays.editOverlay(grabberPointLightCircleX, { visible: false }); + Overlays.editOverlay(grabberPointLightCircleY, { visible: false }); + Overlays.editOverlay(grabberPointLightCircleZ, { visible: false }); + Overlays.editOverlay(grabberPointLightT, { visible: false }); + Overlays.editOverlay(grabberPointLightB, { visible: false }); + Overlays.editOverlay(grabberPointLightL, { visible: false }); + Overlays.editOverlay(grabberPointLightR, { visible: false }); + Overlays.editOverlay(grabberPointLightF, { visible: false }); + Overlays.editOverlay(grabberPointLightN, { visible: false }); + } else if (properties.type == "Light" && properties.isSpotlight == false) { + var stretchHandlesVisible = false; + var extendedStretchHandlesVisible = false; + Overlays.editOverlay(grabberPointLightT, { + position: TOP, + rotation: rotation, + visible: true, + }); + Overlays.editOverlay(grabberPointLightB, { + position: BOTTOM, + rotation: rotation, + visible: true, + }); + Overlays.editOverlay(grabberPointLightL, { + position: LEFT, + rotation: rotation, + visible: true, + }); + Overlays.editOverlay(grabberPointLightR, { + position: RIGHT, + rotation: rotation, + visible: true, + }); + Overlays.editOverlay(grabberPointLightF, { + position: FAR, + rotation: rotation, + visible: true, + }); + Overlays.editOverlay(grabberPointLightN, { + position: NEAR, + rotation: rotation, + visible: true, + }); + Overlays.editOverlay(grabberPointLightCircleX, { + position: position, + rotation: Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(0, 90, 0)), + dimensions: { x: properties.dimensions.z, y: properties.dimensions.z, z: 1 }, + visible: true, + }); + Overlays.editOverlay(grabberPointLightCircleY, { + position: position, + rotation: Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(90, 0, 0)), + dimensions: { x: properties.dimensions.z, y: properties.dimensions.z, z: 1 }, + visible: true, + }); + Overlays.editOverlay(grabberPointLightCircleZ, { + position: position, + rotation: rotation, + dimensions: { x: properties.dimensions.z, y: properties.dimensions.z, z: 1 }, + visible: true, + }); + + Overlays.editOverlay(grabberSpotLightRadius, { visible: false }); + Overlays.editOverlay(grabberSpotLightL, { visible: false }); + Overlays.editOverlay(grabberSpotLightR, { visible: false }); + Overlays.editOverlay(grabberSpotLightT, { visible: false }); + Overlays.editOverlay(grabberSpotLightB, { visible: false }); + Overlays.editOverlay(grabberSpotLightCircle, { visible: false }); + Overlays.editOverlay(grabberSpotLightLineL, { visible: false }); + Overlays.editOverlay(grabberSpotLightLineR, { visible: false }); + Overlays.editOverlay(grabberSpotLightLineT, { visible: false }); + Overlays.editOverlay(grabberSpotLightLineB, { visible: false }); + } else { + Overlays.editOverlay(grabberSpotLightCenter, { visible: false }); + Overlays.editOverlay(grabberSpotLightRadius, { visible: false }); + Overlays.editOverlay(grabberSpotLightL, { visible: false }); + Overlays.editOverlay(grabberSpotLightR, { visible: false }); + Overlays.editOverlay(grabberSpotLightT, { visible: false }); + Overlays.editOverlay(grabberSpotLightB, { visible: false }); + Overlays.editOverlay(grabberSpotLightCircle, { visible: false }); + Overlays.editOverlay(grabberSpotLightLineL, { visible: false }); + Overlays.editOverlay(grabberSpotLightLineR, { visible: false }); + Overlays.editOverlay(grabberSpotLightLineT, { visible: false }); + Overlays.editOverlay(grabberSpotLightLineB, { visible: false }); + + Overlays.editOverlay(grabberPointLightCircleX, { visible: false }); + Overlays.editOverlay(grabberPointLightCircleY, { visible: false }); + Overlays.editOverlay(grabberPointLightCircleZ, { visible: false }); + Overlays.editOverlay(grabberPointLightT, { visible: false }); + Overlays.editOverlay(grabberPointLightB, { visible: false }); + Overlays.editOverlay(grabberPointLightL, { visible: false }); + Overlays.editOverlay(grabberPointLightR, { visible: false }); + Overlays.editOverlay(grabberPointLightF, { visible: false }); + Overlays.editOverlay(grabberPointLightN, { visible: false }); + } + } + + + Overlays.editOverlay(grabberLBN, { visible: stretchHandlesVisible, rotation: rotation, position: LBN }); Overlays.editOverlay(grabberRBN, { visible: stretchHandlesVisible, rotation: rotation, position: RBN }); Overlays.editOverlay(grabberLBF, { visible: stretchHandlesVisible, rotation: rotation, position: LBF }); @@ -1422,7 +1662,7 @@ SelectionDisplay = (function () { // direction - direction to stretch in // pivot - point to use as a pivot // offset - the position of the overlay tool relative to the selections center position - var makeStretchTool = function(stretchMode, direction, pivot, offset) { + var makeStretchTool = function(stretchMode, direction, pivot, offset, customOnMove) { var signs = { x: direction.x < 0 ? -1 : (direction.x > 0 ? 1 : 0), y: direction.y < 0 ? -1 : (direction.y > 0 ? 1 : 0), @@ -1554,7 +1794,7 @@ SelectionDisplay = (function () { }; var onMove = function(event) { - var proportional = spaceMode == SPACE_WORLD || event.isShifted; + var proportional = spaceMode == SPACE_WORLD || event.isShifted || activeTool.mode == "STRETCH_RADIUS"; var position, dimensions, rotation; if (spaceMode == SPACE_LOCAL) { @@ -1577,61 +1817,66 @@ SelectionDisplay = (function () { vector = vec3Mult(mask, vector); - vector = grid.snapToSpacing(vector); - - var changeInDimensions = Vec3.multiply(-1, vec3Mult(signs, vector)); - var newDimensions; - if (proportional) { - var absX = Math.abs(changeInDimensions.x); - var absY = Math.abs(changeInDimensions.y); - var absZ = Math.abs(changeInDimensions.z); - var pctChange = 0; - if (absX > absY && absX > absZ) { - pctChange = changeInDimensions.x / initialProperties.dimensions.x; - pctChange = changeInDimensions.x / initialDimensions.x; - } else if (absY > absZ) { - pctChange = changeInDimensions.y / initialProperties.dimensions.y; - pctChange = changeInDimensions.y / initialDimensions.y; - } else { - pctChange = changeInDimensions.z / initialProperties.dimensions.z; - pctChange = changeInDimensions.z / initialDimensions.z; - } - pctChange += 1.0; - newDimensions = Vec3.multiply(pctChange, initialDimensions); + if (customOnMove) { + var change = Vec3.multiply(-1, vec3Mult(signs, vector)); + customOnMove(vector, change); } else { - newDimensions = Vec3.sum(initialDimensions, changeInDimensions); - } - - newDimensions.x = Math.max(newDimensions.x, MINIMUM_DIMENSION); - newDimensions.y = Math.max(newDimensions.y, MINIMUM_DIMENSION); - newDimensions.z = Math.max(newDimensions.z, MINIMUM_DIMENSION); - - var changeInPosition = Vec3.multiplyQbyV(rotation, vec3Mult(deltaPivot, changeInDimensions)); - var newPosition = Vec3.sum(initialPosition, changeInPosition); - - for (var i = 0; i < SelectionManager.selections.length; i++) { - Entities.editEntity(SelectionManager.selections[i], { - position: newPosition, - dimensions: newDimensions, - }); - } + vector = grid.snapToSpacing(vector); - var wantDebug = false; - if (wantDebug) { - print(stretchMode); - Vec3.print(" newIntersection:", newIntersection); - Vec3.print(" vector:", vector); - Vec3.print(" oldPOS:", oldPOS); - Vec3.print(" newPOS:", newPOS); - Vec3.print(" changeInDimensions:", changeInDimensions); - Vec3.print(" newDimensions:", newDimensions); + var changeInDimensions = Vec3.multiply(-1, vec3Mult(signs, vector)); + var newDimensions; + if (proportional) { + var absX = Math.abs(changeInDimensions.x); + var absY = Math.abs(changeInDimensions.y); + var absZ = Math.abs(changeInDimensions.z); + var pctChange = 0; + if (absX > absY && absX > absZ) { + pctChange = changeInDimensions.x / initialProperties.dimensions.x; + pctChange = changeInDimensions.x / initialDimensions.x; + } else if (absY > absZ) { + pctChange = changeInDimensions.y / initialProperties.dimensions.y; + pctChange = changeInDimensions.y / initialDimensions.y; + } else { + pctChange = changeInDimensions.z / initialProperties.dimensions.z; + pctChange = changeInDimensions.z / initialDimensions.z; + } + pctChange += 1.0; + newDimensions = Vec3.multiply(pctChange, initialDimensions); + } else { + newDimensions = Vec3.sum(initialDimensions, changeInDimensions); + } + + newDimensions.x = Math.max(newDimensions.x, MINIMUM_DIMENSION); + newDimensions.y = Math.max(newDimensions.y, MINIMUM_DIMENSION); + newDimensions.z = Math.max(newDimensions.z, MINIMUM_DIMENSION); + + var changeInPosition = Vec3.multiplyQbyV(rotation, vec3Mult(deltaPivot, changeInDimensions)); + var newPosition = Vec3.sum(initialPosition, changeInPosition); + + for (var i = 0; i < SelectionManager.selections.length; i++) { + Entities.editEntity(SelectionManager.selections[i], { + position: newPosition, + dimensions: newDimensions, + }); + } - Vec3.print(" changeInPosition:", changeInPosition); - Vec3.print(" newPosition:", newPosition); + var wantDebug = false; + if (wantDebug) { + print(stretchMode); + Vec3.print(" newIntersection:", newIntersection); + Vec3.print(" vector:", vector); + Vec3.print(" oldPOS:", oldPOS); + Vec3.print(" newPOS:", newPOS); + Vec3.print(" changeInDimensions:", changeInDimensions); + Vec3.print(" newDimensions:", newDimensions); + + Vec3.print(" changeInPosition:", changeInPosition); + Vec3.print(" newPosition:", newPosition); + } + + SelectionManager._update(); } - SelectionManager._update(); - }; return { @@ -1642,15 +1887,57 @@ SelectionDisplay = (function () { }; }; - function addStretchTool(overlay, mode, pivot, direction, offset) { + function addStretchTool(overlay, mode, pivot, direction, offset, handleMove) { if (!pivot) { pivot = direction; } - var tool = makeStretchTool(mode, direction, pivot, offset); + var tool = makeStretchTool(mode, direction, pivot, offset, handleMove); addGrabberTool(overlay, tool); } + function cutoffStretchFunc(vector, change) { + vector = change; + Vec3.print("Radius stretch: ", vector); + var length = vector.x + vector.y + vector.z; + var props = selectionManager.savedProperties[selectionManager.selections[0].id]; + + var radius = props.dimensions.z / 2; + var originalCutoff = props.cutoff; + + var originalSize = radius * Math.tan(originalCutoff * (Math.PI / 180)); + var newSize = originalSize + length; + var cutoff = Math.atan2(newSize, radius) * 180 / Math.PI; + + Entities.editEntity(selectionManager.selections[0], { + cutoff: cutoff, + }); + + SelectionManager._update(); + }; + + function radiusStretchFunc(vector, change) { + var props = selectionManager.savedProperties[selectionManager.selections[0].id]; + + // Find the axis being adjusted + var size; + if (Math.abs(change.x) > 0) { + size = props.dimensions.x + change.x; + } else if (Math.abs(change.y) > 0) { + size = props.dimensions.y + change.y; + } else if (Math.abs(change.z) > 0) { + size = props.dimensions.z + change.z; + } + + var newDimensions = { x: size, y: size, z: size }; + + Entities.editEntity(selectionManager.selections[0], { + dimensions: newDimensions, + }); + + SelectionManager._update(); + } + addStretchTool(grabberNEAR, "STRETCH_NEAR", { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: -1 }); addStretchTool(grabberFAR, "STRETCH_FAR", { x: 0, y: 0, z: -1 }, { x: 0, y: 0, z: -1 }, { x: 0, y: 0, z: 1 }); addStretchTool(grabberTOP, "STRETCH_TOP", { x: 0, y: -1, z: 0 }, { x: 0, y: -1, z: 0 }, { x: 0, y: 1, z: 0 }); @@ -1658,6 +1945,19 @@ SelectionDisplay = (function () { addStretchTool(grabberRIGHT, "STRETCH_RIGHT", { x: -1, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }); addStretchTool(grabberLEFT, "STRETCH_LEFT", { x: 1, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }); + addStretchTool(grabberSpotLightRadius, "STRETCH_RADIUS", { x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: -1 }); + addStretchTool(grabberSpotLightT, "STRETCH_CUTOFF_T", { x: 0, y: 0, z: 0 }, { x: 0, y: -1, z: 0 }, { x: 0, y: 1, z: 0 }, cutoffStretchFunc); + addStretchTool(grabberSpotLightB, "STRETCH_CUTOFF_B", { x: 0, y: 0, z: 0 }, { x: 0, y: 1, z: 0 }, { x: 0, y: -1, z: 0 }, cutoffStretchFunc); + addStretchTool(grabberSpotLightL, "STRETCH_CUTOFF_L", { x: 0, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }, cutoffStretchFunc); + addStretchTool(grabberSpotLightR, "STRETCH_CUTOFF_R", { x: 0, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }, cutoffStretchFunc); + + addStretchTool(grabberPointLightT, "STRETCH_RADIUS_T", { x: 0, y: 0, z: 0 }, { x: 0, y: -1, z: 0 }, { x: 0, y: 0, z: 1 }, radiusStretchFunc); + addStretchTool(grabberPointLightB, "STRETCH_RADIUS_B", { x: 0, y: 0, z: 0 }, { x: 0, y: 1, z: 0 }, { x: 0, y: 0, z: 1 }, radiusStretchFunc); + addStretchTool(grabberPointLightL, "STRETCH_RADIUS_L", { x: 0, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }, { x: 0, y: 0, z: 1 }, radiusStretchFunc); + addStretchTool(grabberPointLightR, "STRETCH_RADIUS_R", { x: 0, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }, { x: 0, y: 0, z: 1 }, radiusStretchFunc); + addStretchTool(grabberPointLightF, "STRETCH_RADIUS_F", { x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: -1 }, { x: 0, y: 0, z: 1 }, radiusStretchFunc); + addStretchTool(grabberPointLightN, "STRETCH_RADIUS_N", { x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: -1 }, radiusStretchFunc); + addStretchTool(grabberLBN, "STRETCH_LBN", null, {x: 1, y: 0, z: 1}, { x: -1, y: -1, z: -1 }); addStretchTool(grabberRBN, "STRETCH_RBN", null, {x: -1, y: 0, z: 1}, { x: 1, y: -1, z: -1 }); addStretchTool(grabberLBF, "STRETCH_LBF", null, {x: 1, y: 0, z: -1}, { x: -1, y: -1, z: 1 }); @@ -2399,6 +2699,17 @@ SelectionDisplay = (function () { case grabberEdgeNL: case grabberEdgeFR: case grabberEdgeFL: + case grabberSpotLightRadius: + case grabberSpotLightT: + case grabberSpotLightB: + case grabberSpotLightL: + case grabberSpotLightR: + case grabberPointLightT: + case grabberPointLightB: + case grabberPointLightR: + case grabberPointLightL: + case grabberPointLightN: + case grabberPointLightF: pickedColor = grabberColorEdge; pickedAlpha = grabberAlpha; highlightNeeded = true; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 943f2bd491..af6c600bf8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -269,6 +269,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _dependencyManagerIsSetup(setupEssentials(argc, argv)), _window(new MainWindow(desktop())), _toolWindow(NULL), + _friendsWindow(NULL), _datagramProcessor(), _undoStack(), _undoStackScriptingInterface(&_undoStack), @@ -310,8 +311,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _isVSyncOn(true), _aboutToQuit(false), _notifiedPacketVersionMismatchThisDomain(false), - _domainConnectionRefusals(QList()), - _friendsWindow(NULL) + _domainConnectionRefusals(QList()) { #ifdef Q_OS_WIN installNativeEventFilter(&MyNativeEventFilter::getInstance()); @@ -977,6 +977,11 @@ void Application::keyPressEvent(QKeyEvent* event) { _myAvatar->setDriveKeys(UP, 1.0f); break; + case Qt::Key_F: { + _physicsEngine.dumpNextStats(); + break; + } + case Qt::Key_Asterisk: Menu::getInstance()->triggerOption(MenuOption::Stars); break; @@ -1184,6 +1189,7 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_Comma: { renderCollisionHulls = !renderCollisionHulls; + break; } default: @@ -2617,7 +2623,12 @@ glm::vec3 Application::getSunDirection() { return skyStage->getSunLight()->getDirection(); } +// FIXME, preprocessor guard this check to occur only in DEBUG builds +static QThread * activeRenderingThread = nullptr; + void Application::updateShadowMap() { + activeRenderingThread = QThread::currentThread(); + PerformanceTimer perfTimer("shadowMap"); QOpenGLFramebufferObject* fbo = DependencyManager::get()->getShadowFramebufferObject(); fbo->bind(); @@ -2712,6 +2723,10 @@ void Application::updateShadowMap() { glLoadIdentity(); glOrtho(minima.x, maxima.x, minima.y, maxima.y, -maxima.z, -minima.z); + glm::mat4 projAgain; + glGetFloatv(GL_PROJECTION_MATRIX, (GLfloat*)&projAgain); + + glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); @@ -2767,6 +2782,7 @@ void Application::updateShadowMap() { fbo->release(); glViewport(0, 0, _glWidget->getDeviceWidth(), _glWidget->getDeviceHeight()); + activeRenderingThread = nullptr; } const GLfloat WORLD_AMBIENT_COLOR[] = { 0.525f, 0.525f, 0.6f }; @@ -2827,9 +2843,6 @@ QImage Application::renderAvatarBillboard() { return image; } -// FIXME, preprocessor guard this check to occur only in DEBUG builds -static QThread * activeRenderingThread = nullptr; - ViewFrustum* Application::getViewFrustum() { #ifdef DEBUG if (QThread::currentThread() == activeRenderingThread) { diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 3ea4723801..13db6d28dd 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -337,8 +337,13 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode, bool // simple frustum check float boundingRadius = getBillboardSize(); - ViewFrustum* frustum = (renderMode == Avatar::SHADOW_RENDER_MODE) ? - Application::getInstance()->getShadowViewFrustum() : Application::getInstance()->getDisplayViewFrustum(); + + ViewFrustum* frustum = nullptr; + if (renderMode == Avatar::SHADOW_RENDER_MODE) { + frustum = Application::getInstance()->getShadowViewFrustum(); + } else { + frustum = Application::getInstance()->getDisplayViewFrustum(); + } if (frustum->sphereInFrustum(getPosition(), boundingRadius) == ViewFrustum::OUTSIDE) { return; } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index f92523c58f..e4eb6e7869 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1005,22 +1005,25 @@ void MyAvatar::renderBody(ViewFrustum* renderFrustum, RenderMode renderMode, boo Camera *camera = Application::getInstance()->getCamera(); const glm::vec3 cameraPos = camera->getPosition(); - // Set near clip distance according to skeleton model dimensions if first person and there is no separate head model. - if (shouldRenderHead(cameraPos, renderMode) || !getHead()->getFaceModel().getURL().isEmpty()) { - renderFrustum->setNearClip(DEFAULT_NEAR_CLIP); - } else { - float clipDistance = _skeletonModel.getHeadClipDistance(); - if (OculusManager::isConnected()) { - // If avatar is horizontally in front of camera, increase clip distance by the amount it is in front. - glm::vec3 cameraToAvatar = _position - cameraPos; - cameraToAvatar.y = 0.0f; - glm::vec3 cameraLookAt = camera->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f); - float headOffset = glm::dot(cameraLookAt, cameraToAvatar); - if (headOffset > 0) { - clipDistance += headOffset; + // Only tweak the frustum near far if it's not shadow + if (renderMode != SHADOW_RENDER_MODE) { + // Set near clip distance according to skeleton model dimensions if first person and there is no separate head model. + if (shouldRenderHead(cameraPos, renderMode) || !getHead()->getFaceModel().getURL().isEmpty()) { + renderFrustum->setNearClip(DEFAULT_NEAR_CLIP); + } else { + float clipDistance = _skeletonModel.getHeadClipDistance(); + if (OculusManager::isConnected()) { + // If avatar is horizontally in front of camera, increase clip distance by the amount it is in front. + glm::vec3 cameraToAvatar = _position - cameraPos; + cameraToAvatar.y = 0.0f; + glm::vec3 cameraLookAt = camera->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f); + float headOffset = glm::dot(cameraLookAt, cameraToAvatar); + if (headOffset > 0) { + clipDistance += headOffset; + } } + renderFrustum->setNearClip(clipDistance); } - renderFrustum->setNearClip(clipDistance); } // Render the body's voxels and head diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index db10298583..5fbd0b6a0b 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -443,7 +443,6 @@ void OculusManager::configureCamera(Camera& camera, int screenWidth, int screenH camera.setFieldOfView(atan(_eyeFov[0].UpTan) * DEGREES_PER_RADIAN * 2.0f); } -static bool timerActive = false; //Displays everything for the oculus, frame timing must be active void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &position, Camera& whichCamera) { auto glCanvas = Application::getInstance()->getGLWidget(); @@ -463,6 +462,7 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p // (does not seem to work on OSX with SDK based distortion) // FIXME can't use a static object here, because it will cause a crash when the // query attempts deconstruct after the GL context is gone. + static bool timerActive = false; static QOpenGLTimerQuery timerQuery; if (!timerQuery.isCreated()) { timerQuery.create(); @@ -671,7 +671,6 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p #endif // No DK2, no message. - char latency2Text[128] = ""; { float latencies[5] = {}; if (debugFrame && ovrHmd_GetFloatArray(_ovrHmd, "DK2Latency", latencies, 5) == 5) diff --git a/interface/src/devices/OculusManager.h b/interface/src/devices/OculusManager.h index 6c23776e18..fe2da31231 100644 --- a/interface/src/devices/OculusManager.h +++ b/interface/src/devices/OculusManager.h @@ -31,6 +31,10 @@ class Text3DOverlay; #define OVR_CLIENT_DISTORTION 1 +// Direct HMD mode is currently only supported on windows and some linux systems will +// misbehave if we try to enable the Oculus SDK at all, so isolate support for Direct +// mode only to windows for now +#ifdef Q_OS_WIN // On Win32 platforms, enabling Direct HMD requires that the SDK be // initialized before the GL context is set up, but this breaks v-sync // for any application that has a Direct mode enable Rift connected @@ -40,6 +44,7 @@ class Text3DOverlay; // caveat that it will break v-sync in NON-VR mode if you have an Oculus // Rift connect and in Direct mode #define OVR_DIRECT_MODE 1 +#endif /// Handles interaction with the Oculus Rift. diff --git a/interface/src/ui/ScriptEditorWindow.cpp b/interface/src/ui/ScriptEditorWindow.cpp index 858f26f843..424b649b02 100644 --- a/interface/src/ui/ScriptEditorWindow.cpp +++ b/interface/src/ui/ScriptEditorWindow.cpp @@ -30,6 +30,7 @@ #include "Application.h" #include "FlowLayout.h" #include "JSConsole.h" +#include "PathUtils.h" ScriptEditorWindow::ScriptEditorWindow(QWidget* parent) : QWidget(parent), @@ -40,6 +41,7 @@ ScriptEditorWindow::ScriptEditorWindow(QWidget* parent) : setAttribute(Qt::WA_DeleteOnClose); _ScriptEditorWindowUI->setupUi(this); + this->setWindowFlags(Qt::Tool); addScriptEditorWidget("New script"); connect(_loadMenu, &QMenu::aboutToShow, this, &ScriptEditorWindow::loadMenuAboutToShow); @@ -54,6 +56,11 @@ ScriptEditorWindow::ScriptEditorWindow(QWidget* parent) : connect(new QShortcut(QKeySequence("Ctrl+O"), this), &QShortcut::activated, this, &ScriptEditorWindow::loadScriptClicked); connect(new QShortcut(QKeySequence("F5"), this), &QShortcut::activated, this, &ScriptEditorWindow::toggleRunScriptClicked); + _ScriptEditorWindowUI->loadButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + "icons/load-script.svg"))); + _ScriptEditorWindowUI->newButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + "icons/new-script.svg"))); + _ScriptEditorWindowUI->saveButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + "icons/save-script.svg"))); + _ScriptEditorWindowUI->toggleRunButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + "icons/start-script.svg"))); + QWidget* console = new JSConsole(this); console->setFixedHeight(CONSOLE_HEIGHT); this->layout()->addWidget(console); diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp index 132f58b6f0..0e20fd8ce4 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ b/interface/src/ui/overlays/Circle3DOverlay.cpp @@ -98,6 +98,9 @@ void Circle3DOverlay::render(RenderArgs* args) { const float MAX_COLOR = 255.0f; glm::vec4 color(colorX.red / MAX_COLOR, colorX.green / MAX_COLOR, colorX.blue / MAX_COLOR, alpha); + bool colorChanged = colorX.red != _lastColor.red || colorX.green != _lastColor.green || colorX.blue != _lastColor.blue; + _lastColor = colorX; + glDisable(GL_LIGHTING); glm::vec3 position = getPosition(); @@ -131,7 +134,7 @@ void Circle3DOverlay::render(RenderArgs* args) { _quadVerticesID = geometryCache->allocateID(); } - if (geometryChanged) { + if (geometryChanged || colorChanged) { QVector points; @@ -170,7 +173,7 @@ void Circle3DOverlay::render(RenderArgs* args) { _lineVerticesID = geometryCache->allocateID(); } - if (geometryChanged) { + if (geometryChanged || colorChanged) { QVector points; float angle = startAt; diff --git a/interface/src/ui/overlays/Circle3DOverlay.h b/interface/src/ui/overlays/Circle3DOverlay.h index 10b7a5dbfa..fa9ecd0f25 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.h +++ b/interface/src/ui/overlays/Circle3DOverlay.h @@ -70,6 +70,7 @@ protected: int _majorTicksVerticesID; int _minorTicksVerticesID; + xColor _lastColor; float _lastStartAt; float _lastEndAt; float _lastOuterRadius; diff --git a/interface/ui/scriptEditorWindow.ui b/interface/ui/scriptEditorWindow.ui index 0379f51e97..1e50aaef0b 100644 --- a/interface/ui/scriptEditorWindow.ui +++ b/interface/ui/scriptEditorWindow.ui @@ -29,7 +29,16 @@ 0 - + + 0 + + + 0 + + + 0 + + 0 @@ -54,11 +63,6 @@ New - - - ../resources/icons/new-script.svg - ../resources/icons/new-script.svg../resources/icons/new-script.svg - 32 @@ -87,10 +91,6 @@ Load - - - ../resources/icons/load-script.svg../resources/icons/load-script.svg - 32 @@ -134,10 +134,6 @@ Save - - - ../resources/icons/save-script.svg../resources/icons/save-script.svg - 32 @@ -160,10 +156,6 @@ Run/Stop - - - ../resources/icons/start-script.svg../resources/icons/start-script.svg - 32 diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 0dcab2c81b..94470f48e4 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1281,6 +1281,9 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) { } void AudioClient::checkDevices() { +# ifdef Q_OS_LINUX + // on linux, this makes the audio stream hiccup +# else QVector inputDevices = getDeviceNames(QAudio::AudioInput); QVector outputDevices = getDeviceNames(QAudio::AudioOutput); @@ -1290,6 +1293,7 @@ void AudioClient::checkDevices() { emit deviceChanged(); } +# endif } void AudioClient::loadSettings() { diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index a73f652282..dc9c27e093 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -524,9 +524,9 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef } } else { if (useMeters) { - READ_ENTITY_PROPERTY_SETTER(PROP_DIMENSIONS, glm::vec3, setDimensions); + READ_ENTITY_PROPERTY_SETTER(PROP_DIMENSIONS, glm::vec3, updateDimensions); } else { - READ_ENTITY_PROPERTY_SETTER(PROP_DIMENSIONS, glm::vec3, setDimensionsInDomainUnits); + READ_ENTITY_PROPERTY_SETTER(PROP_DIMENSIONS, glm::vec3, updateDimensionsInDomainUnits); } } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 16ece64a4d..446c5ab22f 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -920,6 +920,11 @@ int EntityTree::processEraseMessage(const QByteArray& dataByteArray, const Share EntityItemID entityItemID(entityID); entityItemIDsToDelete << entityItemID; + + if (wantEditLogging()) { + qDebug() << "User [" << sourceNode->getUUID() << "] deleting entity. ID:" << entityItemID; + } + } deleteEntities(entityItemIDsToDelete, true, true); } @@ -959,6 +964,11 @@ int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, cons EntityItemID entityItemID(entityID); entityItemIDsToDelete << entityItemID; + + if (wantEditLogging()) { + qDebug() << "User [" << sourceNode->getUUID() << "] deleting entity. ID:" << entityItemID; + } + } deleteEntities(entityItemIDsToDelete, true, true); } diff --git a/libraries/entities/src/LightEntityItem.cpp b/libraries/entities/src/LightEntityItem.cpp index c7a4ef79d7..996677141a 100644 --- a/libraries/entities/src/LightEntityItem.cpp +++ b/libraries/entities/src/LightEntityItem.cpp @@ -41,8 +41,16 @@ LightEntityItem::LightEntityItem(const EntityItemID& entityItemID, const EntityI } void LightEntityItem::setDimensions(const glm::vec3& value) { - float maxDimension = glm::max(value.x, value.y, value.z); - _dimensions = glm::vec3(maxDimension, maxDimension, maxDimension); + if (_isSpotlight) { + // If we are a spotlight, treat the z value as our radius or length, and + // recalculate the x/y dimensions to properly encapsulate the spotlight. + const float length = value.z; + const float width = length * glm::tan(glm::radians(_cutoff)); + _dimensions = glm::vec3(width, width, length); + } else { + float maxDimension = glm::max(value.x, value.y, value.z); + _dimensions = glm::vec3(maxDimension, maxDimension, maxDimension); + } } @@ -58,6 +66,33 @@ EntityItemProperties LightEntityItem::getProperties() const { return properties; } +void LightEntityItem::setIsSpotlight(bool value) { + if (value != _isSpotlight) { + _isSpotlight = value; + + if (_isSpotlight) { + const float length = _dimensions.z; + const float width = length * glm::tan(glm::radians(_cutoff)); + _dimensions = glm::vec3(width, width, length); + } else { + float maxDimension = glm::max(_dimensions.x, _dimensions.y, _dimensions.z); + _dimensions = glm::vec3(maxDimension, maxDimension, maxDimension); + } + } +} + +void LightEntityItem::setCutoff(float value) { + _cutoff = glm::clamp(value, 0.0f, 90.0f); + + 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 width = length * glm::tan(glm::radians(_cutoff)); + _dimensions = glm::vec3(width, width, length); + } +} + bool LightEntityItem::setProperties(const EntityItemProperties& properties) { bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class diff --git a/libraries/entities/src/LightEntityItem.h b/libraries/entities/src/LightEntityItem.h index cdbdb59ece..373f88286e 100644 --- a/libraries/entities/src/LightEntityItem.h +++ b/libraries/entities/src/LightEntityItem.h @@ -56,7 +56,7 @@ public: } bool getIsSpotlight() const { return _isSpotlight; } - void setIsSpotlight(bool value) { _isSpotlight = value; } + void setIsSpotlight(bool value); float getIntensity() const { return _intensity; } void setIntensity(float value) { _intensity = value; } @@ -65,7 +65,7 @@ public: void setExponent(float value) { _exponent = value; } float getCutoff() const { return _cutoff; } - void setCutoff(float value) { _cutoff = value; } + void setCutoff(float value); static bool getLightsArePickable() { return _lightsArePickable; } static void setLightsArePickable(bool value) { _lightsArePickable = value; } diff --git a/libraries/gpu/src/gpu/GLBackendShader.cpp b/libraries/gpu/src/gpu/GLBackendShader.cpp index bcfdc4f36c..3f794575fe 100755 --- a/libraries/gpu/src/gpu/GLBackendShader.cpp +++ b/libraries/gpu/src/gpu/GLBackendShader.cpp @@ -104,7 +104,7 @@ void makeBindings(GLBackend::GLShader* shader) { loc = glGetUniformBlockIndex(glprogram, "transformCameraBuffer"); if (loc >= 0) { glUniformBlockBinding(glprogram, loc, gpu::TRANSFORM_CAMERA_SLOT); - shader->_transformCameraSlot = gpu::TRANSFORM_OBJECT_SLOT; + shader->_transformCameraSlot = gpu::TRANSFORM_CAMERA_SLOT; } #endif } diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp index 9b513c96f1..00ef0f9e1d 100644 --- a/libraries/networking/src/DataServerAccountInfo.cpp +++ b/libraries/networking/src/DataServerAccountInfo.cpp @@ -15,6 +15,7 @@ #include #include "DataServerAccountInfo.h" +#pragma clang diagnostic ignored "-Wdeprecated-declarations" DataServerAccountInfo::DataServerAccountInfo() : _accessToken(), diff --git a/libraries/networking/src/RSAKeypairGenerator.cpp b/libraries/networking/src/RSAKeypairGenerator.cpp index a51a17c0ca..bfe16cc9b8 100644 --- a/libraries/networking/src/RSAKeypairGenerator.cpp +++ b/libraries/networking/src/RSAKeypairGenerator.cpp @@ -16,6 +16,7 @@ #include #include "RSAKeypairGenerator.h" +#pragma clang diagnostic ignored "-Wdeprecated-declarations" RSAKeypairGenerator::RSAKeypairGenerator(QObject* parent) : QObject(parent) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index cdff02820d..1d7a84c177 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -257,6 +257,7 @@ btPairCachingGhostObject* CharacterController::getGhostObject() { } bool CharacterController::recoverFromPenetration(btCollisionWorld* collisionWorld) { + BT_PROFILE("recoverFromPenetration"); // Here we must refresh the overlapping paircache as the penetrating movement itself or the // previous recovery iteration might have used setWorldTransform and pushed us into an object // that is not in the previous cache contents from the last timestep, as will happen if we @@ -355,6 +356,7 @@ bool CharacterController::recoverFromPenetration(btCollisionWorld* collisionWorl void CharacterController::scanDown(btCollisionWorld* world) { + BT_PROFILE("scanDown"); // we test with downward raycast and if we don't find floor close enough then turn on "hover" btKinematicClosestNotMeRayResultCallback callback(_ghostObject); callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; @@ -374,6 +376,7 @@ void CharacterController::scanDown(btCollisionWorld* world) { } void CharacterController::stepUp(btCollisionWorld* world) { + BT_PROFILE("stepUp"); // phase 1: up // compute start and end @@ -440,6 +443,7 @@ void CharacterController::updateTargetPositionBasedOnCollision(const btVector3& } void CharacterController::stepForward(btCollisionWorld* collisionWorld, const btVector3& movement) { + BT_PROFILE("stepForward"); // phase 2: forward _targetPosition = _currentPosition + movement; @@ -496,6 +500,7 @@ void CharacterController::stepForward(btCollisionWorld* collisionWorld, const bt } void CharacterController::stepDown(btCollisionWorld* collisionWorld, btScalar dt) { + BT_PROFILE("stepDown"); // phase 3: down // // The "stepDown" phase first makes a normal sweep down that cancels the lift from the "stepUp" phase. @@ -607,6 +612,7 @@ void CharacterController::warp(const btVector3& origin) { void CharacterController::preStep(btCollisionWorld* collisionWorld) { + BT_PROFILE("preStep"); if (!_enabled) { return; } @@ -627,6 +633,7 @@ void CharacterController::preStep(btCollisionWorld* collisionWorld) { } void CharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar dt) { + BT_PROFILE("playerStep"); if (!_enabled) { return; // no motion } @@ -875,6 +882,7 @@ void CharacterController::updateShapeIfNecessary() { } void CharacterController::preSimulation(btScalar timeStep) { + BT_PROFILE("preSimulation"); if (_enabled && _dynamicsWorld) { glm::quat rotation = _avatarData->getOrientation(); _currentUp = quatRotate(glmToBullet(rotation), LOCAL_UP_AXIS); @@ -897,6 +905,7 @@ void CharacterController::preSimulation(btScalar timeStep) { } void CharacterController::postSimulation() { + BT_PROFILE("postSimulation"); if (_enabled && _ghostObject) { const btTransform& avatarTransform = _ghostObject->getWorldTransform(); glm::quat rotation = bulletToGLM(avatarTransform.getRotation()); diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 467b51560f..67e4e0616f 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -155,6 +155,7 @@ void PhysicsEngine::clearEntitiesInternal() { // end EntitySimulation overrides void PhysicsEngine::relayIncomingChangesToSimulation() { + BT_PROFILE("incomingChanges"); // process incoming changes QSet::iterator stateItr = _incomingChanges.begin(); while (stateItr != _incomingChanges.end()) { @@ -287,66 +288,76 @@ void PhysicsEngine::init(EntityEditPacketSender* packetSender) { } void PhysicsEngine::stepSimulation() { - lock(); - // NOTE: the grand order of operations is: - // (1) pull incoming changes - // (2) step simulation - // (3) synchronize outgoing motion states - // (4) send outgoing packets - - // This is step (1) pull incoming changes - relayIncomingChangesToSimulation(); - - const int MAX_NUM_SUBSTEPS = 4; - const float MAX_TIMESTEP = (float)MAX_NUM_SUBSTEPS * PHYSICS_ENGINE_FIXED_SUBSTEP; - float dt = 1.0e-6f * (float)(_clock.getTimeMicroseconds()); - _clock.reset(); - float timeStep = btMin(dt, MAX_TIMESTEP); - - // TODO: move character->preSimulation() into relayIncomingChanges - if (_characterController) { - if (_characterController->needsRemoval()) { - _characterController->setDynamicsWorld(NULL); - } - _characterController->updateShapeIfNecessary(); - if (_characterController->needsAddition()) { - _characterController->setDynamicsWorld(_dynamicsWorld); - } - _characterController->preSimulation(timeStep); - } - - // This is step (2) step simulation - int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP); - _numSubsteps += (uint32_t)numSubsteps; - stepNonPhysicalKinematics(usecTimestampNow()); - unlock(); - - // TODO: make all of this harvest stuff into one function: relayOutgoingChanges() - if (numSubsteps > 0) { - // This is step (3) which is done outside of stepSimulation() so we can lock _entityTree. - // - // Unfortunately we have to unlock the simulation (above) before we try to lock the _entityTree - // to avoid deadlock -- the _entityTree may try to lock its EntitySimulation (from which this - // PhysicsEngine derives) when updating/adding/deleting entities so we need to wait for our own - // lock on the tree before we re-lock ourselves. - // - // TODO: untangle these lock sequences. - _entityTree->lockForWrite(); + { lock(); - _dynamicsWorld->synchronizeMotionStates(); + CProfileManager::Reset(); + BT_PROFILE("stepSimulation"); + // NOTE: the grand order of operations is: + // (1) pull incoming changes + // (2) step simulation + // (3) synchronize outgoing motion states + // (4) send outgoing packets - if (_characterController) { - _characterController->postSimulation(); - } - - unlock(); - _entityTree->unlock(); + // This is step (1) pull incoming changes + relayIncomingChangesToSimulation(); - computeCollisionEvents(); + const int MAX_NUM_SUBSTEPS = 4; + const float MAX_TIMESTEP = (float)MAX_NUM_SUBSTEPS * PHYSICS_ENGINE_FIXED_SUBSTEP; + float dt = 1.0e-6f * (float)(_clock.getTimeMicroseconds()); + _clock.reset(); + float timeStep = btMin(dt, MAX_TIMESTEP); + + // TODO: move character->preSimulation() into relayIncomingChanges + if (_characterController) { + if (_characterController->needsRemoval()) { + _characterController->setDynamicsWorld(NULL); + } + _characterController->updateShapeIfNecessary(); + if (_characterController->needsAddition()) { + _characterController->setDynamicsWorld(_dynamicsWorld); + } + _characterController->preSimulation(timeStep); + } + + // This is step (2) step simulation + int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP); + _numSubsteps += (uint32_t)numSubsteps; + stepNonPhysicalKinematics(usecTimestampNow()); + unlock(); + + // TODO: make all of this harvest stuff into one function: relayOutgoingChanges() + if (numSubsteps > 0) { + BT_PROFILE("postSimulation"); + // This is step (3) which is done outside of stepSimulation() so we can lock _entityTree. + // + // Unfortunately we have to unlock the simulation (above) before we try to lock the _entityTree + // to avoid deadlock -- the _entityTree may try to lock its EntitySimulation (from which this + // PhysicsEngine derives) when updating/adding/deleting entities so we need to wait for our own + // lock on the tree before we re-lock ourselves. + // + // TODO: untangle these lock sequences. + _entityTree->lockForWrite(); + lock(); + _dynamicsWorld->synchronizeMotionStates(); + + if (_characterController) { + _characterController->postSimulation(); + } + + unlock(); + _entityTree->unlock(); + + computeCollisionEvents(); + } + } + if (_dumpNextStats) { + _dumpNextStats = false; + CProfileManager::dumpAll(); } } void PhysicsEngine::stepNonPhysicalKinematics(const quint64& now) { + BT_PROFILE("nonPhysicalKinematics"); QSet::iterator stateItr = _nonPhysicalKinematicObjects.begin(); while (stateItr != _nonPhysicalKinematicObjects.end()) { ObjectMotionState* motionState = *stateItr; @@ -358,6 +369,7 @@ void PhysicsEngine::stepNonPhysicalKinematics(const quint64& now) { // TODO?: need to occasionally scan for stopped non-physical kinematics objects void PhysicsEngine::computeCollisionEvents() { + BT_PROFILE("computeCollisionEvents"); // update all contacts every frame int numManifolds = _collisionDispatcher->getNumManifolds(); for (int i = 0; i < numManifolds; ++i) { diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 0661b47d3a..d7d3278286 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -86,6 +86,8 @@ public: void setCharacterController(CharacterController* character); + void dumpNextStats() { _dumpNextStats = true; } + private: /// \param motionState pointer to Object's MotionState void removeObjectFromBullet(ObjectMotionState* motionState); @@ -121,6 +123,8 @@ private: /// character collisions CharacterController* _characterController = NULL; + + bool _dumpNextStats = false; }; #endif // hifi_PhysicsEngine_h diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp index c7ee9ce2a0..a345b915db 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp @@ -15,6 +15,8 @@ * Copied and modified from btDiscreteDynamicsWorld.cpp by AndrewMeadows on 2014.11.12. * */ +#include + #include "ThreadSafeDynamicsWorld.h" ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld( @@ -25,50 +27,51 @@ ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld( : btDiscreteDynamicsWorld(dispatcher, pairCache, constraintSolver, collisionConfiguration) { } -int ThreadSafeDynamicsWorld::stepSimulation( btScalar timeStep, int maxSubSteps, btScalar fixedTimeStep) { - int subSteps = 0; - if (maxSubSteps) { - //fixed timestep with interpolation - m_fixedTimeStep = fixedTimeStep; - m_localTime += timeStep; - if (m_localTime >= fixedTimeStep) - { - subSteps = int( m_localTime / fixedTimeStep); - m_localTime -= subSteps * fixedTimeStep; - } - } else { - //variable timestep - fixedTimeStep = timeStep; - m_localTime = m_latencyMotionStateInterpolation ? 0 : timeStep; - m_fixedTimeStep = 0; - if (btFuzzyZero(timeStep)) - { - subSteps = 0; - maxSubSteps = 0; - } else - { - subSteps = 1; - maxSubSteps = 1; - } - } +int ThreadSafeDynamicsWorld::stepSimulation( btScalar timeStep, int maxSubSteps, btScalar fixedTimeStep) { + BT_PROFILE("stepSimulation"); + int subSteps = 0; + if (maxSubSteps) { + //fixed timestep with interpolation + m_fixedTimeStep = fixedTimeStep; + m_localTime += timeStep; + if (m_localTime >= fixedTimeStep) + { + subSteps = int( m_localTime / fixedTimeStep); + m_localTime -= subSteps * fixedTimeStep; + } + } else { + //variable timestep + fixedTimeStep = timeStep; + m_localTime = m_latencyMotionStateInterpolation ? 0 : timeStep; + m_fixedTimeStep = 0; + if (btFuzzyZero(timeStep)) + { + subSteps = 0; + maxSubSteps = 0; + } else + { + subSteps = 1; + maxSubSteps = 1; + } + } - /*//process some debugging flags - if (getDebugDrawer()) { - btIDebugDraw* debugDrawer = getDebugDrawer (); - gDisableDeactivation = (debugDrawer->getDebugMode() & btIDebugDraw::DBG_NoDeactivation) != 0; - }*/ - if (subSteps) { - //clamp the number of substeps, to prevent simulation grinding spiralling down to a halt - int clampedSimulationSteps = (subSteps > maxSubSteps)? maxSubSteps : subSteps; + /*//process some debugging flags + if (getDebugDrawer()) { + btIDebugDraw* debugDrawer = getDebugDrawer (); + gDisableDeactivation = (debugDrawer->getDebugMode() & btIDebugDraw::DBG_NoDeactivation) != 0; + }*/ + if (subSteps) { + //clamp the number of substeps, to prevent simulation grinding spiralling down to a halt + int clampedSimulationSteps = (subSteps > maxSubSteps)? maxSubSteps : subSteps; - saveKinematicState(fixedTimeStep*clampedSimulationSteps); + saveKinematicState(fixedTimeStep*clampedSimulationSteps); - applyGravity(); + applyGravity(); - for (int i=0;i_viewFrustum) { glm::mat4 proj; - args->_viewFrustum->evalProjectionMatrix(proj); + // If for easier debug depending on the pass + if (mode == RenderArgs::SHADOW_RENDER_MODE) { + args->_viewFrustum->evalProjectionMatrix(proj); + } else { + args->_viewFrustum->evalProjectionMatrix(proj); + } batch.setProjectionTransform(proj); } @@ -678,7 +684,9 @@ bool Model::renderCore(float alpha, RenderMode mode, RenderArgs* args) { if (_transforms.empty()) { _transforms.push_back(Transform()); } + _transforms[0] = _viewState->getViewTransform(); + // apply entity translation offset to the viewTransform in one go (it's a preTranslate because viewTransform goes from world to eye space) _transforms[0].preTranslate(-_translation); @@ -699,7 +707,7 @@ bool Model::renderCore(float alpha, RenderMode mode, RenderArgs* args) { GLBATCH(glDisable)(GL_BLEND); GLBATCH(glEnable)(GL_ALPHA_TEST); - + if (mode == SHADOW_RENDER_MODE) { GLBATCH(glAlphaFunc)(GL_EQUAL, 0.0f); } @@ -1710,14 +1718,19 @@ void Model::startScene(RenderArgs::RenderSide renderSide) { } } -void Model::setupBatchTransform(gpu::Batch& batch) { +void Model::setupBatchTransform(gpu::Batch& batch, RenderArgs* args) { // Capture the view matrix once for the rendering of this model if (_transforms.empty()) { _transforms.push_back(Transform()); } + + // We should be able to use the Frustum viewpoint onstead of the "viewTransform" + // but it s still buggy in some cases, so let's s wait and fix it... _transforms[0] = _viewState->getViewTransform(); + _transforms[0].preTranslate(-_translation); + batch.setViewTransform(_transforms[0]); } @@ -1738,7 +1751,12 @@ void Model::endScene(RenderMode mode, RenderArgs* args) { if (args) { glm::mat4 proj; - args->_viewFrustum->evalProjectionMatrix(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); @@ -2286,6 +2304,7 @@ void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, f skinLocations = &_skinLocations; if (mode == SHADOW_RENDER_MODE) { program = _shadowProgram; + locations = &_shadowLocations; skinProgram = _skinShadowProgram; skinLocations = &_skinShadowLocations; } else if (translucent && alphaThreshold == 0.0f) { @@ -2376,7 +2395,7 @@ int Model::renderMeshesForModelsInScene(gpu::Batch& batch, RenderMode mode, bool pickPrograms(batch, mode, translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, args, locations, skinLocations); pickProgramsNeeded = false; } - model->setupBatchTransform(batch); + model->setupBatchTransform(batch, args); meshPartsRendered += model->renderMeshesFromList(list, batch, mode, translucent, alphaThreshold, args, locations, skinLocations); } } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 5dbd0b03c2..f1bbf151cd 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -357,6 +357,7 @@ private: static Locations _specularMapLocations; static Locations _normalSpecularMapLocations; static Locations _translucentLocations; + static Locations _shadowLocations; static Locations _lightmapLocations; static Locations _lightmapNormalMapLocations; @@ -377,8 +378,9 @@ private: static SkinLocations _skinNormalMapLocations; static SkinLocations _skinSpecularMapLocations; static SkinLocations _skinNormalSpecularMapLocations; - static SkinLocations _skinShadowLocations; static SkinLocations _skinTranslucentLocations; + static SkinLocations _skinShadowLocations; + static void initSkinProgram(ProgramObject& program, SkinLocations& locations); static void initSkinProgram(gpu::ShaderPointer& program, SkinLocations& locations); @@ -463,7 +465,7 @@ private: bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args = NULL, bool forceRenderMeshes = false); - void setupBatchTransform(gpu::Batch& batch); + void setupBatchTransform(gpu::Batch& batch, RenderArgs* args); QVector* pickMeshList(bool translucent, float alphaThreshold, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned); int renderMeshesFromList(QVector& list, gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold,