diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 06fb5c4f47..72f2567809 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -760,47 +760,6 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url statsString += QString(" Total: %1 nodes\r\n") .arg(locale.toString((uint)checkSum).rightJustified(16, ' ')); -#ifdef BLENDED_UNION_CHILDREN - statsString += "\r\n"; - statsString += "OctreeElement Children Encoding Statistics...\r\n"; - - statsString += QString().sprintf(" Single or No Children: %10.llu nodes (%5.2f%%)\r\n", - OctreeElement::getSingleChildrenCount(), - ((float)OctreeElement::getSingleChildrenCount() / (float)nodeCount) * AS_PERCENT)); - statsString += QString().sprintf(" Two Children as Offset: %10.llu nodes (%5.2f%%)\r\n", - OctreeElement::getTwoChildrenOffsetCount(), - ((float)OctreeElement::getTwoChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT)); - statsString += QString().sprintf(" Two Children as External: %10.llu nodes (%5.2f%%)\r\n", - OctreeElement::getTwoChildrenExternalCount(), - ((float)OctreeElement::getTwoChildrenExternalCount() / (float)nodeCount) * AS_PERCENT); - statsString += QString().sprintf(" Three Children as Offset: %10.llu nodes (%5.2f%%)\r\n", - OctreeElement::getThreeChildrenOffsetCount(), - ((float)OctreeElement::getThreeChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT); - statsString += QString().sprintf(" Three Children as External: %10.llu nodes (%5.2f%%)\r\n", - OctreeElement::getThreeChildrenExternalCount(), - ((float)OctreeElement::getThreeChildrenExternalCount() / (float)nodeCount) * AS_PERCENT); - statsString += QString().sprintf(" Children as External Array: %10.llu nodes (%5.2f%%)\r\n", - OctreeElement::getExternalChildrenCount(), - ((float)OctreeElement::getExternalChildrenCount() / (float)nodeCount) * AS_PERCENT); - - checkSum = OctreeElement::getSingleChildrenCount() + - OctreeElement::getTwoChildrenOffsetCount() + OctreeElement::getTwoChildrenExternalCount() + - OctreeElement::getThreeChildrenOffsetCount() + OctreeElement::getThreeChildrenExternalCount() + - OctreeElement::getExternalChildrenCount(); - - statsString += " ----------------\r\n"; - statsString += QString().sprintf(" Total: %10.llu nodes\r\n", checkSum); - statsString += QString().sprintf(" Expected: %10.lu nodes\r\n", nodeCount); - - statsString += "\r\n"; - statsString += "In other news....\r\n"; - - statsString += QString().sprintf("could store 4 children internally: %10.llu nodes\r\n", - OctreeElement::getCouldStoreFourChildrenInternally()); - statsString += QString().sprintf("could NOT store 4 children internally: %10.llu nodes\r\n", - OctreeElement::getCouldNotStoreFourChildrenInternally()); -#endif - statsString += "\r\n\r\n"; statsString += "\r\n"; statsString += ""; diff --git a/examples/utilities/tools/cookies.js b/examples/utilities/tools/cookies.js old mode 100755 new mode 100644 index 2ec34fcec8..0fdae01c5e --- a/examples/utilities/tools/cookies.js +++ b/examples/utilities/tools/cookies.js @@ -1,651 +1,665 @@ -// -// cookies.js -// -// version 1.0 -// -// Created by Sam Gateau, 4/1/2015 -// A simple ui panel that present a list of porperties and the proper widget to edit it -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// The Slider class -Slider = function(x,y,width,thumbSize) { - this.background = Overlays.addOverlay("text", { - backgroundColor: { red: 125, green: 125, blue: 255 }, - x: x, - y: y, - width: width, - height: thumbSize, - alpha: 1.0, - backgroundAlpha: 0.5, - visible: true - }); - this.thumb = Overlays.addOverlay("text", { - backgroundColor: { red: 255, green: 255, blue: 255 }, - x: x, - y: y, - width: thumbSize, - height: thumbSize, - alpha: 1.0, - backgroundAlpha: 1.0, - visible: true - }); - - - this.thumbSize = thumbSize; - this.thumbHalfSize = 0.5 * thumbSize; - - this.minThumbX = x + this.thumbHalfSize; - this.maxThumbX = x + width - this.thumbHalfSize; - this.thumbX = this.minThumbX; - - this.minValue = 0.0; - this.maxValue = 1.0; - - this.clickOffsetX = 0; - this.isMoving = false; - - this.updateThumb = function() { - thumbTruePos = this.thumbX - 0.5 * this.thumbSize; - Overlays.editOverlay(this.thumb, { x: thumbTruePos } ); - }; - - this.isClickableOverlayItem = function(item) { - return (item == this.thumb) || (item == this.background); - }; - - this.onMouseMoveEvent = function(event) { - if (this.isMoving) { - newThumbX = event.x - this.clickOffsetX; - if (newThumbX < this.minThumbX) { - newThumbX = this.minThumbX; - } - if (newThumbX > this.maxThumbX) { - newThumbX = this.maxThumbX; - } - this.thumbX = newThumbX; - this.updateThumb(); - this.onValueChanged(this.getValue()); - } - }; - - this.onMousePressEvent = function(event, clickedOverlay) { - if (!this.isClickableOverlayItem(clickedOverlay)) { - this.isMoving = false; - return; - } - this.isMoving = true; - var clickOffset = event.x - this.thumbX; - if ((clickOffset > -this.thumbHalfSize) && (clickOffset < this.thumbHalfSize)) { - this.clickOffsetX = clickOffset; - } else { - this.clickOffsetX = 0; - this.thumbX = event.x; - this.updateThumb(); - this.onValueChanged(this.getValue()); - } - - }; - - this.onMouseReleaseEvent = function(event) { - this.isMoving = false; - }; - - // Public members: - - this.setNormalizedValue = function(value) { - if (value < 0.0) { - this.thumbX = this.minThumbX; - } else if (value > 1.0) { - this.thumbX = this.maxThumbX; - } else { - this.thumbX = value * (this.maxThumbX - this.minThumbX) + this.minThumbX; - } - this.updateThumb(); - }; - this.getNormalizedValue = function() { - return (this.thumbX - this.minThumbX) / (this.maxThumbX - this.minThumbX); - }; - - this.setValue = function(value) { - var normValue = (value - this.minValue) / (this.maxValue - this.minValue); - this.setNormalizedValue(normValue); - }; - - this.getValue = function() { - return this.getNormalizedValue() * (this.maxValue - this.minValue) + this.minValue; - }; - - this.onValueChanged = function(value) {}; - - this.setMaxValue = function(maxValue) { - if (this.maxValue == maxValue) { - return; - } - var currentVal = this.getValue(); - this.maxValue = maxValue; - this.setValue(currentVal); - } - this.setMinValue = function(minValue) { - if (this.minValue == minValue) { - return; - } - var currentVal = this.getValue(); - this.minValue = minValue; - this.setValue(currentVal); - } - - this.destroy = function() { - Overlays.deleteOverlay(this.background); - Overlays.deleteOverlay(this.thumb); - }; - - this.setThumbColor = function(color) { - Overlays.editOverlay(this.thumb, {backgroundColor: { red: color.x*255, green: color.y*255, blue: color.z*255 }}); - }; - this.setBackgroundColor = function(color) { - Overlays.editOverlay(this.background, {backgroundColor: { red: color.x*255, green: color.y*255, blue: color.z*255 }}); - }; -} - -// The Checkbox class -Checkbox = function(x,y,thumbSize) { - - this.thumb = Overlays.addOverlay("text", { - backgroundColor: { red: 255, green: 255, blue: 255 }, - x: x, - y: y, - width: thumbSize, - height: thumbSize, - alpha: 1.0, - backgroundAlpha: 1.0, - visible: true - }); - this.background = Overlays.addOverlay("text", { - backgroundColor: { red: 125, green: 125, blue: 255 }, - x: x, - y: y, - width: thumbSize * 2, - height: thumbSize, - alpha: 1.0, - backgroundAlpha: 0.5, - visible: true - }); - - this.thumbSize = thumbSize; - this.thumbHalfSize = 0.5 * thumbSize; - - this.minThumbX = x + this.thumbHalfSize; - this.maxThumbX = x + thumbSize * 2 - this.thumbHalfSize; - this.thumbX = this.minThumbX; - - this.minValue = 0.0; - this.maxValue = 1.0; - - this.clickOffsetX = 0; - this.isMoving = false; - - this.updateThumb = function() { - thumbTruePos = this.thumbX - 0.5 * this.thumbSize; - Overlays.editOverlay(this.thumb, { x: thumbTruePos } ); - }; - - this.isClickableOverlayItem = function(item) { - return item == this.background; - }; - - this.onMouseMoveEvent = function(event) { - if (this.isMoving) { - newThumbX = event.x - this.clickOffsetX; - if (newThumbX < this.minThumbX) { - newThumbX = this.minThumbX; - } - if (newThumbX > this.maxThumbX) { - newThumbX = this.maxThumbX; - } - this.thumbX = newThumbX; - this.updateThumb(); - this.onValueChanged(this.getValue()); - } - }; - - this.onMousePressEvent = function(event, clickedOverlay) { - if (this.background != clickedOverlay) { - this.isMoving = false; - return; - } - this.isMoving = true; - var clickOffset = event.x - this.thumbX; - if ((clickOffset > -this.thumbHalfSize) && (clickOffset < this.thumbHalfSize)) { - this.clickOffsetX = clickOffset; - } else { - this.clickOffsetX = 0; - this.thumbX = event.x; - this.updateThumb(); - this.onValueChanged(this.getValue()); - } - - }; - - this.onMouseReleaseEvent = function(event) { - this.isMoving = false; - }; - - // Public members: - - this.setNormalizedValue = function(value) { - if (value < 0.0) { - this.thumbX = this.minThumbX; - } else if (value > 1.0) { - this.thumbX = this.maxThumbX; - } else { - this.thumbX = value * (this.maxThumbX - this.minThumbX) + this.minThumbX; - } - this.updateThumb(); - }; - this.getNormalizedValue = function() { - return (this.thumbX - this.minThumbX) / (this.maxThumbX - this.minThumbX); - }; - - this.setValue = function(value) { - var normValue = (value - this.minValue) / (this.maxValue - this.minValue); - this.setNormalizedValue(normValue); - }; - - this.getValue = function() { - return this.getNormalizedValue() * (this.maxValue - this.minValue) + this.minValue; - }; - - this.onValueChanged = function(value) {}; - - this.destroy = function() { - Overlays.deleteOverlay(this.background); - Overlays.deleteOverlay(this.thumb); - }; -} - -// The ColorBox class -ColorBox = function(x,y,width,thumbSize) { - var self = this; - - var slideHeight = thumbSize / 3; - var sliderWidth = width; - this.red = new Slider(x, y, width, slideHeight); - this.green = new Slider(x, y + slideHeight, width, slideHeight); - this.blue = new Slider(x, y + 2 * slideHeight, width, slideHeight); - this.red.setBackgroundColor({x: 1, y: 0, z: 0}); - this.green.setBackgroundColor({x: 0, y: 1, z: 0}); - this.blue.setBackgroundColor({x: 0, y: 0, z: 1}); - - this.isClickableOverlayItem = function(item) { - return this.red.isClickableOverlayItem(item) - || this.green.isClickableOverlayItem(item) - || this.blue.isClickableOverlayItem(item); - }; - - this.onMouseMoveEvent = function(event) { - this.red.onMouseMoveEvent(event); - this.green.onMouseMoveEvent(event); - this.blue.onMouseMoveEvent(event); - }; - - this.onMousePressEvent = function(event, clickedOverlay) { - this.red.onMousePressEvent(event, clickedOverlay); - if (this.red.isMoving) { - return; - } - - this.green.onMousePressEvent(event, clickedOverlay); - if (this.green.isMoving) { - return; - } - - this.blue.onMousePressEvent(event, clickedOverlay); - }; - - this.onMouseReleaseEvent = function(event) { - this.red.onMouseReleaseEvent(event); - this.green.onMouseReleaseEvent(event); - this.blue.onMouseReleaseEvent(event); - }; - - this.setterFromWidget = function(value) { - var color = self.getValue(); - self.onValueChanged(color); - self.updateRGBSliders(color); - }; - - this.red.onValueChanged = this.setterFromWidget; - this.green.onValueChanged = this.setterFromWidget; - this.blue.onValueChanged = this.setterFromWidget; - - this.updateRGBSliders = function(color) { - this.red.setThumbColor({x: color.x, y: 0, z: 0}); - this.green.setThumbColor({x: 0, y: color.y, z: 0}); - this.blue.setThumbColor({x: 0, y: 0, z: color.z}); - }; - - // Public members: - this.setValue = function(value) { - this.red.setValue(value.x); - this.green.setValue(value.y); - this.blue.setValue(value.z); - this.updateRGBSliders(value); - }; - - this.getValue = function() { - var value = {x:this.red.getValue(), y:this.green.getValue(),z:this.blue.getValue()}; - return value; - }; - - this.destroy = function() { - this.red.destroy(); - this.green.destroy(); - this.blue.destroy(); - }; - - this.onValueChanged = function(value) {}; -} - -// The DirectionBox class -DirectionBox = function(x,y,width,thumbSize) { - var self = this; - - var slideHeight = thumbSize / 2; - var sliderWidth = width; - this.yaw = new Slider(x, y, width, slideHeight); - this.pitch = new Slider(x, y + slideHeight, width, slideHeight); - - this.yaw.setThumbColor({x: 1, y: 0, z: 0}); - this.yaw.minValue = -180; - this.yaw.maxValue = +180; - - this.pitch.setThumbColor({x: 0, y: 0, z: 1}); - this.pitch.minValue = -1; - this.pitch.maxValue = +1; - - this.isClickableOverlayItem = function(item) { - return this.yaw.isClickableOverlayItem(item) - || this.pitch.isClickableOverlayItem(item); - }; - - this.onMouseMoveEvent = function(event) { - this.yaw.onMouseMoveEvent(event); - this.pitch.onMouseMoveEvent(event); - }; - - this.onMousePressEvent = function(event, clickedOverlay) { - this.yaw.onMousePressEvent(event, clickedOverlay); - if (this.yaw.isMoving) { - return; - } - this.pitch.onMousePressEvent(event, clickedOverlay); - }; - - this.onMouseReleaseEvent = function(event) { - this.yaw.onMouseReleaseEvent(event); - this.pitch.onMouseReleaseEvent(event); - }; - - this.setterFromWidget = function(value) { - var yawPitch = self.getValue(); - self.onValueChanged(yawPitch); - }; - - this.yaw.onValueChanged = this.setterFromWidget; - this.pitch.onValueChanged = this.setterFromWidget; - - // Public members: - this.setValue = function(direction) { - var flatXZ = Math.sqrt(direction.x * direction.x + direction.z * direction.z); - if (flatXZ > 0.0) { - var flatX = direction.x / flatXZ; - var flatZ = direction.z / flatXZ; - var yaw = Math.acos(flatX) * 180 / Math.PI; - if (flatZ < 0) { - yaw = -yaw; - } - this.yaw.setValue(yaw); - } - this.pitch.setValue(direction.y); - }; - - this.getValue = function() { - var dirZ = this.pitch.getValue(); - var yaw = this.yaw.getValue() * Math.PI / 180; - var cosY = Math.sqrt(1 - dirZ*dirZ); - var value = {x:cosY * Math.cos(yaw), y:dirZ, z: cosY * Math.sin(yaw)}; - return value; - }; - - this.destroy = function() { - this.yaw.destroy(); - this.pitch.destroy(); - }; - - this.onValueChanged = function(value) {}; -} - -var textFontSize = 16; - -function PanelItem(name, setter, getter, displayer, x, y, textWidth, valueWidth, height) { - this.name = name; - - - this.displayer = typeof displayer !== 'undefined' ? displayer : function(value) { return value.toFixed(2); }; - - var topMargin = (height - textFontSize); - this.title = Overlays.addOverlay("text", { - backgroundColor: { red: 255, green: 255, blue: 255 }, - x: x, - y: y, - width: textWidth, - height: height, - alpha: 1.0, - backgroundAlpha: 0.5, - visible: true, - text: name, - font: {size: textFontSize}, - topMargin: topMargin, - }); - - this.value = Overlays.addOverlay("text", { - backgroundColor: { red: 255, green: 255, blue: 255 }, - x: x + textWidth, - y: y, - width: valueWidth, - height: height, - alpha: 1.0, - backgroundAlpha: 0.5, - visible: true, - text: this.displayer(getter()), - font: {size: textFontSize}, - topMargin: topMargin - - }); - this.getter = getter; - - this.setter = function(value) { - setter(value); - Overlays.editOverlay(this.value, {text: this.displayer(getter())}); - if (this.widget) { - this.widget.setValue(value); - } - }; - this.setterFromWidget = function(value) { - setter(value); - // ANd loop back the value after the final setter has been called - var value = getter(); - if (this.widget) { - this.widget.setValue(value); - } - Overlays.editOverlay(this.value, {text: this.displayer(value)}); - }; - - - this.widget = null; - - this.destroy = function() { - Overlays.deleteOverlay(this.title); - Overlays.deleteOverlay(this.value); - if (this.widget != null) { - this.widget.destroy(); - } - } -} - -var textWidth = 180; -var valueWidth = 100; -var widgetWidth = 300; -var rawHeight = 20; -var rawYDelta = rawHeight * 1.5; - -Panel = function(x, y) { - - this.x = x; - this.y = y; - this.nextY = y; - - this.widgetX = x + textWidth + valueWidth; - - this.items = new Array(); - this.activeWidget = null; - - this.mouseMoveEvent = function(event) { - if (this.activeWidget) { - this.activeWidget.onMouseMoveEvent(event); - } - }; - - // we also handle click detection in our mousePressEvent() - this.mousePressEvent = function(event) { - // Make sure we quitted previous widget - if (this.activeWidget) { - this.activeWidget.onMouseReleaseEvent(event); - } - this.activeWidget = null; - - var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); - - // If the user clicked any of the slider background then... - for (var i in this.items) { - var widget = this.items[i].widget; - - if (widget.isClickableOverlayItem(clickedOverlay)) { - this.activeWidget = widget; - this.activeWidget.onMousePressEvent(event, clickedOverlay); - // print("clicked... widget=" + i); - break; - } - } - }; - - this.mouseReleaseEvent = function(event) { - if (this.activeWidget) { - this.activeWidget.onMouseReleaseEvent(event); - } - this.activeWidget = null; - }; - - this.newSlider = function(name, minValue, maxValue, setValue, getValue, displayValue) { - - var item = new PanelItem(name, setValue, getValue, displayValue, this.x, this.nextY, textWidth, valueWidth, rawHeight); - - var slider = new Slider(this.widgetX, this.nextY, widgetWidth, rawHeight); - slider.minValue = minValue; - slider.maxValue = maxValue; - - - item.widget = slider; - item.widget.onValueChanged = function(value) { item.setterFromWidget(value); }; - item.setter(getValue()); - this.items[name] = item; - this.nextY += rawYDelta; - // print("created Item... slider=" + name); - }; - - this.newCheckbox = function(name, setValue, getValue, displayValue) { - - var item = new PanelItem(name, setValue, getValue, displayValue, this.x, this.nextY, textWidth, valueWidth, rawHeight); - - var checkbox = new Checkbox(this.widgetX, this.nextY, rawHeight); - - item.widget = checkbox; - item.widget.onValueChanged = function(value) { item.setterFromWidget(value); }; - item.setter(getValue()); - this.items[name] = item; - this.nextY += rawYDelta; - // print("created Item... slider=" + name); - }; - - this.newColorBox = function(name, setValue, getValue, displayValue) { - - var item = new PanelItem(name, setValue, getValue, displayValue, this.x, this.nextY, textWidth, valueWidth, rawHeight); - - var colorBox = new ColorBox(this.widgetX, this.nextY, widgetWidth, rawHeight); - - item.widget = colorBox; - item.widget.onValueChanged = function(value) { item.setterFromWidget(value); }; - item.setter(getValue()); - this.items[name] = item; - this.nextY += rawYDelta; - // print("created Item... colorBox=" + name); - }; - - this.newDirectionBox= function(name, setValue, getValue, displayValue) { - - var item = new PanelItem(name, setValue, getValue, displayValue, this.x, this.nextY, textWidth, valueWidth, rawHeight); - - var directionBox = new DirectionBox(this.widgetX, this.nextY, widgetWidth, rawHeight); - - item.widget = directionBox; - item.widget.onValueChanged = function(value) { item.setterFromWidget(value); }; - item.setter(getValue()); - this.items[name] = item; - this.nextY += rawYDelta; - // print("created Item... directionBox=" + name); - }; - - this.destroy = function() { - for (var i in this.items) { - this.items[i].destroy(); - } - } - - this.set = function(name, value) { - var item = this.items[name]; - if (item != null) { - return item.setter(value); - } - return null; - } - - this.get = function(name) { - var item = this.items[name]; - if (item != null) { - return item.getter(); - } - return null; - } - - this.getWidget = function(name) { - var item = this.items[name]; - if (item != null) { - return item.widget; - } - return null; - } - - this.update = function(name) { - var item = this.items[name]; - if (item != null) { - return item.setter(item.getter()); - } - return null; - } - -}; - - +// +// cookies.js +// +// version 2.0 +// +// Created by Sam Gateau, 4/1/2015 +// A simple ui panel that present a list of porperties and the proper widget to edit it +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +// The Slider class +Slider = function(x,y,width,thumbSize) { + this.background = Overlays.addOverlay("text", { + backgroundColor: { red: 200, green: 200, blue: 255 }, + x: x, + y: y, + width: width, + height: thumbSize, + alpha: 1.0, + backgroundAlpha: 0.5, + visible: true + }); + this.thumb = Overlays.addOverlay("text", { + backgroundColor: { red: 255, green: 255, blue: 255 }, + x: x, + y: y, + width: thumbSize, + height: thumbSize, + alpha: 1.0, + backgroundAlpha: 1.0, + visible: true + }); + + + this.thumbSize = thumbSize; + this.thumbHalfSize = 0.5 * thumbSize; + + this.minThumbX = x + this.thumbHalfSize; + this.maxThumbX = x + width - this.thumbHalfSize; + this.thumbX = this.minThumbX; + + this.minValue = 0.0; + this.maxValue = 1.0; + + this.clickOffsetX = 0; + this.isMoving = false; + + this.updateThumb = function() { + thumbTruePos = this.thumbX - 0.5 * this.thumbSize; + Overlays.editOverlay(this.thumb, { x: thumbTruePos } ); + }; + + this.isClickableOverlayItem = function(item) { + return (item == this.thumb) || (item == this.background); + }; + + this.onMouseMoveEvent = function(event) { + if (this.isMoving) { + newThumbX = event.x - this.clickOffsetX; + if (newThumbX < this.minThumbX) { + newThumbX = this.minThumbX; + } + if (newThumbX > this.maxThumbX) { + newThumbX = this.maxThumbX; + } + this.thumbX = newThumbX; + this.updateThumb(); + this.onValueChanged(this.getValue()); + } + }; + + this.onMousePressEvent = function(event, clickedOverlay) { + if (!this.isClickableOverlayItem(clickedOverlay)) { + this.isMoving = false; + return; + } + this.isMoving = true; + var clickOffset = event.x - this.thumbX; + if ((clickOffset > -this.thumbHalfSize) && (clickOffset < this.thumbHalfSize)) { + this.clickOffsetX = clickOffset; + } else { + this.clickOffsetX = 0; + this.thumbX = event.x; + this.updateThumb(); + this.onValueChanged(this.getValue()); + } + + }; + + this.onMouseReleaseEvent = function(event) { + this.isMoving = false; + }; + + + // Public members: + this.setNormalizedValue = function(value) { + if (value < 0.0) { + this.thumbX = this.minThumbX; + } else if (value > 1.0) { + this.thumbX = this.maxThumbX; + } else { + this.thumbX = value * (this.maxThumbX - this.minThumbX) + this.minThumbX; + } + this.updateThumb(); + }; + this.getNormalizedValue = function() { + return (this.thumbX - this.minThumbX) / (this.maxThumbX - this.minThumbX); + }; + + this.setValue = function(value) { + var normValue = (value - this.minValue) / (this.maxValue - this.minValue); + this.setNormalizedValue(normValue); + }; + + this.getValue = function() { + return this.getNormalizedValue() * (this.maxValue - this.minValue) + this.minValue; + }; + + this.onValueChanged = function(value) {}; + + this.destroy = function() { + Overlays.deleteOverlay(this.background); + Overlays.deleteOverlay(this.thumb); + }; + + this.setThumbColor = function(color) { + Overlays.editOverlay(this.thumb, {backgroundColor: { red: color.x*255, green: color.y*255, blue: color.z*255 }}); + }; + this.setBackgroundColor = function(color) { + Overlays.editOverlay(this.background, {backgroundColor: { red: color.x*255, green: color.y*255, blue: color.z*255 }}); + }; +} + +// The Checkbox class +Checkbox = function(x,y,width,thumbSize) { + + this.background = Overlays.addOverlay("text", { + backgroundColor: { red: 125, green: 125, blue: 255 }, + x: x, + y: y, + width: width, + height: thumbSize, + alpha: 1.0, + backgroundAlpha: 0.5, + visible: true + }); + + this.thumb = Overlays.addOverlay("text", { + backgroundColor: { red: 255, green: 255, blue: 255 }, + textFontSize: 10, + x: x, + y: y, + width: thumbSize, + height: thumbSize, + alpha: 1.0, + backgroundAlpha: 1.0, + visible: true + }); + + + this.thumbSize = thumbSize; + var checkX = x + (0.25 * thumbSize); + var checkY = y + (0.25 * thumbSize); + + + var checkMark = Overlays.addOverlay("text", { + backgroundColor: { red: 0, green: 255, blue: 0 }, + x: checkX, + y: checkY, + width: thumbSize / 2.0, + height: thumbSize / 2.0, + alpha: 1.0, + visible: true + }); + + var unCheckMark = Overlays.addOverlay("image", { + backgroundColor: { red: 255, green: 255, blue: 255 }, + x: checkX + 1.0, + y: checkY + 1.0, + width: thumbSize / 2.5, + height: thumbSize / 2.5, + alpha: 1.0, + visible: boxCheckStatus + }); + + + var boxCheckStatus; + var clickedBox = false; + + this.updateThumb = function() { + if (clickedBox) { + boxCheckStatus = !boxCheckStatus; + if (boxCheckStatus) { + Overlays.editOverlay(unCheckMark, { visible: false }); + } else { + Overlays.editOverlay(unCheckMark, { visible: true }); + } + } + }; + + this.isClickableOverlayItem = function(item) { + return (item == this.thumb) || (item == checkMark) || (item == unCheckMark); + }; + + this.onMousePressEvent = function(event, clickedOverlay) { + if (!this.isClickableOverlayItem(clickedOverlay)) { + this.isMoving = false; + clickedBox = false; + return; + } + + clickedBox = true; + this.updateThumb(); + this.onValueChanged(this.getValue()); + }; + + this.onMouseReleaseEvent = function(event) { + this.clickedBox = false; + }; + + // Public members: + this.setNormalizedValue = function(value) { + boxCheckStatus = value; + }; + + this.getNormalizedValue = function() { + return boxCheckStatus; + }; + + this.setValue = function(value) { + this.setNormalizedValue(value); + }; + + this.getValue = function() { + return boxCheckStatus; + }; + + this.onValueChanged = function(value) {}; + + this.destroy = function() { + Overlays.deleteOverlay(this.background); + Overlays.deleteOverlay(this.thumb); + Overlays.deleteOverlay(checkMark); + Overlays.deleteOverlay(unCheckMark); + }; + + this.setThumbColor = function(color) { + Overlays.editOverlay(this.thumb, { red: 255, green: 255, blue: 255 } ); + }; + this.setBackgroundColor = function(color) { + Overlays.editOverlay(this.background, { red: 125, green: 125, blue: 255 }); + }; + +} + +// The ColorBox class +ColorBox = function(x,y,width,thumbSize) { + var self = this; + + var slideHeight = thumbSize / 3; + var sliderWidth = width; + this.red = new Slider(x, y, width, slideHeight); + this.green = new Slider(x, y + slideHeight, width, slideHeight); + this.blue = new Slider(x, y + 2 * slideHeight, width, slideHeight); + this.red.setBackgroundColor({x: 1, y: 0, z: 0}); + this.green.setBackgroundColor({x: 0, y: 1, z: 0}); + this.blue.setBackgroundColor({x: 0, y: 0, z: 1}); + + this.isClickableOverlayItem = function(item) { + return this.red.isClickableOverlayItem(item) + || this.green.isClickableOverlayItem(item) + || this.blue.isClickableOverlayItem(item); + }; + + this.onMouseMoveEvent = function(event) { + this.red.onMouseMoveEvent(event); + this.green.onMouseMoveEvent(event); + this.blue.onMouseMoveEvent(event); + }; + + this.onMousePressEvent = function(event, clickedOverlay) { + this.red.onMousePressEvent(event, clickedOverlay); + if (this.red.isMoving) { + return; + } + + this.green.onMousePressEvent(event, clickedOverlay); + if (this.green.isMoving) { + return; + } + + this.blue.onMousePressEvent(event, clickedOverlay); + }; + + this.onMouseReleaseEvent = function(event) { + this.red.onMouseReleaseEvent(event); + this.green.onMouseReleaseEvent(event); + this.blue.onMouseReleaseEvent(event); + }; + + this.setterFromWidget = function(value) { + var color = self.getValue(); + self.onValueChanged(color); + self.updateRGBSliders(color); + }; + + this.red.onValueChanged = this.setterFromWidget; + this.green.onValueChanged = this.setterFromWidget; + this.blue.onValueChanged = this.setterFromWidget; + + this.updateRGBSliders = function(color) { + this.red.setThumbColor({x: color.x, y: 0, z: 0}); + this.green.setThumbColor({x: 0, y: color.y, z: 0}); + this.blue.setThumbColor({x: 0, y: 0, z: color.z}); + }; + + // Public members: + this.setValue = function(value) { + this.red.setValue(value.x); + this.green.setValue(value.y); + this.blue.setValue(value.z); + this.updateRGBSliders(value); + }; + + this.getValue = function() { + var value = {x:this.red.getValue(), y:this.green.getValue(),z:this.blue.getValue()}; + return value; + }; + + this.destroy = function() { + this.red.destroy(); + this.green.destroy(); + this.blue.destroy(); + }; + + this.onValueChanged = function(value) {}; +} + +// The DirectionBox class +DirectionBox = function(x,y,width,thumbSize) { + var self = this; + + var slideHeight = thumbSize / 2; + var sliderWidth = width; + this.yaw = new Slider(x, y, width, slideHeight); + this.pitch = new Slider(x, y + slideHeight, width, slideHeight); + + this.yaw.setThumbColor({x: 1, y: 0, z: 0}); + this.yaw.minValue = -180; + this.yaw.maxValue = +180; + + this.pitch.setThumbColor({x: 0, y: 0, z: 1}); + this.pitch.minValue = -1; + this.pitch.maxValue = +1; + + this.isClickableOverlayItem = function(item) { + return this.yaw.isClickableOverlayItem(item) + || this.pitch.isClickableOverlayItem(item); + }; + + this.onMouseMoveEvent = function(event) { + this.yaw.onMouseMoveEvent(event); + this.pitch.onMouseMoveEvent(event); + }; + + this.onMousePressEvent = function(event, clickedOverlay) { + this.yaw.onMousePressEvent(event, clickedOverlay); + if (this.yaw.isMoving) { + return; + } + this.pitch.onMousePressEvent(event, clickedOverlay); + }; + + this.onMouseReleaseEvent = function(event) { + this.yaw.onMouseReleaseEvent(event); + this.pitch.onMouseReleaseEvent(event); + }; + + this.setterFromWidget = function(value) { + var yawPitch = self.getValue(); + self.onValueChanged(yawPitch); + }; + + this.yaw.onValueChanged = this.setterFromWidget; + this.pitch.onValueChanged = this.setterFromWidget; + + // Public members: + this.setValue = function(direction) { + var flatXZ = Math.sqrt(direction.x * direction.x + direction.z * direction.z); + if (flatXZ > 0.0) { + var flatX = direction.x / flatXZ; + var flatZ = direction.z / flatXZ; + var yaw = Math.acos(flatX) * 180 / Math.PI; + if (flatZ < 0) { + yaw = -yaw; + } + this.yaw.setValue(yaw); + } + this.pitch.setValue(direction.y); + }; + + this.getValue = function() { + var dirZ = this.pitch.getValue(); + var yaw = this.yaw.getValue() * Math.PI / 180; + var cosY = Math.sqrt(1 - dirZ*dirZ); + var value = {x:cosY * Math.cos(yaw), y:dirZ, z: cosY * Math.sin(yaw)}; + return value; + }; + + this.destroy = function() { + this.yaw.destroy(); + this.pitch.destroy(); + }; + + this.onValueChanged = function(value) {}; +} + +var textFontSize = 12; + +function PanelItem(name, setter, getter, displayer, x, y, textWidth, valueWidth, height) { + //print("creating panel item: " + name); + + this.name = name; + + this.displayer = typeof displayer !== 'undefined' ? displayer : function(value) { + if(value == true) { + return "On"; + } else if (value == false) { + return "Off"; + } + return value.toFixed(2); + }; + + var topMargin = (height - textFontSize); + this.title = Overlays.addOverlay("text", { + backgroundColor: { red: 255, green: 255, blue: 255 }, + x: x, + y: y, + width: textWidth, + height: height, + alpha: 1.0, + backgroundAlpha: 0.5, + visible: true, + text: name, + font: {size: textFontSize}, + topMargin: topMargin, + }); + + this.value = Overlays.addOverlay("text", { + backgroundColor: { red: 255, green: 255, blue: 255 }, + x: x + textWidth, + y: y, + width: valueWidth, + height: height, + alpha: 1.0, + backgroundAlpha: 0.5, + visible: true, + text: this.displayer(getter()), + font: {size: textFontSize}, + topMargin: topMargin + }); + + this.getter = getter; + this.resetValue = getter(); + + this.setter = function(value) { + + setter(value); + + Overlays.editOverlay(this.value, {text: this.displayer(getter())}); + + if (this.widget) { + this.widget.setValue(value); + } + + //print("successfully set value of widget to " + value); + }; + this.setterFromWidget = function(value) { + setter(value); + // ANd loop back the value after the final setter has been called + var value = getter(); + + if (this.widget) { + this.widget.setValue(value); + } + Overlays.editOverlay(this.value, {text: this.displayer(value)}); + }; + + + this.widget = null; + + this.destroy = function() { + Overlays.deleteOverlay(this.title); + Overlays.deleteOverlay(this.value); + if (this.widget != null) { + this.widget.destroy(); + } + } +} + +var textWidth = 180; +var valueWidth = 100; +var widgetWidth = 300; +var rawHeight = 20; +var rawYDelta = rawHeight * 1.5; + +Panel = function(x, y) { + + this.x = x; + this.y = y; + this.nextY = y; + + this.widgetX = x + textWidth + valueWidth; + + this.items = new Array(); + this.activeWidget = null; + + this.mouseMoveEvent = function(event) { + if (this.activeWidget) { + this.activeWidget.onMouseMoveEvent(event); + } + }; + + this.mousePressEvent = function(event) { + // Make sure we quitted previous widget + if (this.activeWidget) { + this.activeWidget.onMouseReleaseEvent(event); + } + this.activeWidget = null; + + var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + + // If the user clicked any of the slider background then... + for (var i in this.items) { + var widget = this.items[i].widget; + + if (widget.isClickableOverlayItem(clickedOverlay)) { + this.activeWidget = widget; + this.activeWidget.onMousePressEvent(event, clickedOverlay); + + break; + } + } + }; + + // Reset panel item upon double-clicking + this.mouseDoublePressEvent = function(event) { + var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + for (var i in this.items) { + + var item = this.items[i]; + var widget = item.widget; + + if (item.title == clickedOverlay || item.value == clickedOverlay) { + widget.updateThumb(); + widget.onValueChanged(item.resetValue); + break; + } + } + } + + this.mouseReleaseEvent = function(event) { + if (this.activeWidget) { + this.activeWidget.onMouseReleaseEvent(event); + } + this.activeWidget = null; + }; + + this.newSlider = function(name, minValue, maxValue, setValue, getValue, displayValue) { + + var item = new PanelItem(name, setValue, getValue, displayValue, this.x, this.nextY, textWidth, valueWidth, rawHeight); + + var slider = new Slider(this.widgetX, this.nextY, widgetWidth, rawHeight); + slider.minValue = minValue; + slider.maxValue = maxValue; + + item.widget = slider; + item.widget.onValueChanged = function(value) { item.setterFromWidget(value); }; + item.setter(getValue()); + this.items[name] = item; + this.nextY += rawYDelta; + }; + + this.newCheckbox = function(name, setValue, getValue, displayValue) { + var display; + if (displayValue == true) { + display = function() {return "On";}; + } else if (displayValue == false) { + display = function() {return "Off";}; + } + + var item = new PanelItem(name, setValue, getValue, display, this.x, this.nextY, textWidth, valueWidth, rawHeight); + + var checkbox = new Checkbox(this.widgetX, this.nextY, widgetWidth, rawHeight); + + item.widget = checkbox; + item.widget.onValueChanged = function(value) { item.setterFromWidget(value); }; + item.setter(getValue()); + this.items[name] = item; + this.nextY += rawYDelta; + //print("created Item... checkbox=" + name); + }; + + this.newColorBox = function(name, setValue, getValue, displayValue) { + + var item = new PanelItem(name, setValue, getValue, displayValue, this.x, this.nextY, textWidth, valueWidth, rawHeight); + + var colorBox = new ColorBox(this.widgetX, this.nextY, widgetWidth, rawHeight); + + item.widget = colorBox; + item.widget.onValueChanged = function(value) { item.setterFromWidget(value); }; + item.setter(getValue()); + this.items[name] = item; + this.nextY += rawYDelta; + // print("created Item... colorBox=" + name); + }; + + this.newDirectionBox= function(name, setValue, getValue, displayValue) { + + var item = new PanelItem(name, setValue, getValue, displayValue, this.x, this.nextY, textWidth, valueWidth, rawHeight); + + var directionBox = new DirectionBox(this.widgetX, this.nextY, widgetWidth, rawHeight); + + item.widget = directionBox; + item.widget.onValueChanged = function(value) { item.setterFromWidget(value); }; + item.setter(getValue()); + this.items[name] = item; + this.nextY += rawYDelta; + // print("created Item... directionBox=" + name); + }; + + this.destroy = function() { + for (var i in this.items) { + this.items[i].destroy(); + } + } + + this.set = function(name, value) { + var item = this.items[name]; + if (item != null) { + return item.setter(value); + } + return null; + } + + this.get = function(name) { + var item = this.items[name]; + if (item != null) { + return item.getter(); + } + return null; + } + + this.update = function(name) { + var item = this.items[name]; + if (item != null) { + return item.setter(item.getter()); + } + return null; + } + +}; + + diff --git a/examples/utilities/tools/renderEngineDebug.js b/examples/utilities/tools/renderEngineDebug.js index 8185a24078..d50a9c545c 100755 --- a/examples/utilities/tools/renderEngineDebug.js +++ b/examples/utilities/tools/renderEngineDebug.js @@ -10,7 +10,7 @@ Script.include("cookies.js"); -var panel = new Panel(10, 800); +var panel = new Panel(10, 100); panel.newSlider("Num Feed Opaques", 0, 1000, function(value) { }, @@ -66,6 +66,12 @@ panel.newSlider("Max Drawn Overlay3Ds", -1, 100, function(value) { return (value); } ); +panel.newCheckbox("Display status", + function(value) { Scene.setEngineDisplayItemStatus(value); }, + function() { return Scene.doEngineDisplayItemStatus(); }, + function(value) { return (value); } +); + var tickTackPeriod = 500; function updateCounters() { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 09608da117..ad063cd928 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3600,6 +3600,8 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se renderContext._maxDrawnTransparentItems = sceneInterface->getEngineMaxDrawnTransparentItems(); renderContext._maxDrawnOverlay3DItems = sceneInterface->getEngineMaxDrawnOverlay3DItems(); + renderContext._drawItemStatus = sceneInterface->doEngineDisplayItemStatus(); + renderArgs->_shouldRender = LODManager::shouldRender; renderContext.args = renderArgs; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 5053f2767b..009790dd2f 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -533,6 +533,7 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::PipelineWarnings); addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::LogExtraTimings); addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::SuppressShortTimings); + addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::ShowRealtimeEntityStats); auto audioIO = DependencyManager::get(); MenuWrapper* audioDebugMenu = developerMenu->addMenu("Audio"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 5f1909d153..2eb269077a 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -267,6 +267,7 @@ namespace MenuOption { const QString ShowDSConnectTable = "Show Domain Connection Timing"; const QString ShowBordersEntityNodes = "Show Entity Nodes"; const QString ShowIKConstraints = "Show IK Constraints"; + const QString ShowRealtimeEntityStats = "Show Realtime Entity Stats"; const QString SimpleShadows = "Simple"; const QString SixenseEnabled = "Enable Hydra Support"; const QString SixenseMouseInput = "Enable Sixense Mouse Input"; diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index d37a0e27c5..7221c9b693 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -25,18 +25,13 @@ // Used to animate the magnification windows -static const float MAG_SPEED = 0.08f; static const quint64 MSECS_TO_USECS = 1000ULL; static const quint64 TOOLTIP_DELAY = 500 * MSECS_TO_USECS; -static const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; static const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f }; static const float reticleSize = TWO_PI / 100.0f; -static const float CONNECTION_STATUS_BORDER_COLOR[] = { 1.0f, 0.0f, 0.0f }; -static const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f; - static const float CURSOR_PIXEL_SIZE = 32.0f; static const float MOUSE_PITCH_RANGE = 1.0f * PI; static const float MOUSE_YAW_RANGE = 0.5f * TWO_PI; diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 63f68b86ce..e7d220893f 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -36,9 +36,6 @@ #include "ui/Stats.h" #include "ui/AvatarInputs.h" -const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; -const int AUDIO_METER_GAP = 5; -const int MUTE_ICON_PADDING = 10; const vec4 CONNECTION_STATUS_BORDER_COLOR{ 1.0f, 0.0f, 0.0f, 0.8f }; const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f; static const float ORTHO_NEAR_CLIP = -10000; diff --git a/interface/src/ui/OctreeStatsDialog.cpp b/interface/src/ui/OctreeStatsDialog.cpp index 462811fc1c..94f91a5ab7 100644 --- a/interface/src/ui/OctreeStatsDialog.cpp +++ b/interface/src/ui/OctreeStatsDialog.cpp @@ -25,7 +25,9 @@ OctreeStatsDialog::OctreeStatsDialog(QWidget* parent, NodeToOctreeSceneStats* model) : QDialog(parent, Qt::Window | Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint), - _model(model) { + _model(model), + _averageUpdatesPerSecond(SAMPLES_PER_SECOND) +{ _statCount = 0; _octreeServerLabelsCount = 0; @@ -50,6 +52,14 @@ OctreeStatsDialog::OctreeStatsDialog(QWidget* parent, NodeToOctreeSceneStats* mo _localElements = AddStatItem("Local Elements"); _localElementsMemory = AddStatItem("Elements Memory"); _sendingMode = AddStatItem("Sending Mode"); + + _processedPackets = AddStatItem("Processed Packets"); + _processedPacketsElements = AddStatItem("Processed Packets Elements"); + _processedPacketsEntities = AddStatItem("Processed Packets Entities"); + _processedPacketsTiming = AddStatItem("Processed Packets Timing"); + + _entityUpdateTime = AddStatItem("Entity Update Time"); + _entityUpdates = AddStatItem("Entity Updates"); layout()->setSizeConstraint(QLayout::SetFixedSize); } @@ -119,6 +129,34 @@ OctreeStatsDialog::~OctreeStatsDialog() { void OctreeStatsDialog::paintEvent(QPaintEvent* event) { + // Processed Entities Related stats + auto entities = Application::getInstance()->getEntities(); + auto entitiesTree = entities->getTree(); + + // Do this ever paint event... even if we don't update + auto totalTrackedEdits = entitiesTree->getTotalTrackedEdits(); + + // track our updated per second + const quint64 SAMPLING_WINDOW = USECS_PER_SECOND / SAMPLES_PER_SECOND; + quint64 now = usecTimestampNow(); + quint64 sinceLastWindow = now - _lastWindowAt; + auto editsInLastWindow = totalTrackedEdits - _lastKnownTrackedEdits; + float sinceLastWindowInSeconds = (float)sinceLastWindow / (float)USECS_PER_SECOND; + float recentUpdatesPerSecond = (float)editsInLastWindow / sinceLastWindowInSeconds; + if (sinceLastWindow > SAMPLING_WINDOW) { + _averageUpdatesPerSecond.updateAverage(recentUpdatesPerSecond); + _lastWindowAt = now; + _lastKnownTrackedEdits = totalTrackedEdits; + } + + // Only refresh our stats every once in a while, unless asked for realtime + quint64 REFRESH_AFTER = Menu::getInstance()->isOptionChecked(MenuOption::ShowRealtimeEntityStats) ? 0 : USECS_PER_SECOND; + quint64 sinceLastRefresh = now - _lastRefresh; + if (sinceLastRefresh < REFRESH_AFTER) { + return QDialog::paintEvent(event); + } + _lastRefresh = now; + // Update labels QLabel* label; @@ -203,9 +241,100 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) { "Leaves: " << qPrintable(serversLeavesString) << ""; label->setText(statsValue.str().c_str()); + // Processed Packets Elements + auto averageElementsPerPacket = entities->getAverageElementsPerPacket(); + auto averageEntitiesPerPacket = entities->getAverageEntitiesPerPacket(); + + auto averagePacketsPerSecond = entities->getAveragePacketsPerSecond(); + auto averageElementsPerSecond = entities->getAverageElementsPerSecond(); + auto averageEntitiesPerSecond = entities->getAverageEntitiesPerSecond(); + + auto averageWaitLockPerPacket = entities->getAverageWaitLockPerPacket(); + auto averageUncompressPerPacket = entities->getAverageUncompressPerPacket(); + auto averageReadBitstreamPerPacket = entities->getAverageReadBitstreamPerPacket(); + + QString averageElementsPerPacketString = locale.toString(averageElementsPerPacket); + QString averageEntitiesPerPacketString = locale.toString(averageEntitiesPerPacket); + + QString averagePacketsPerSecondString = locale.toString(averagePacketsPerSecond); + QString averageElementsPerSecondString = locale.toString(averageElementsPerSecond); + QString averageEntitiesPerSecondString = locale.toString(averageEntitiesPerSecond); + + QString averageWaitLockPerPacketString = locale.toString(averageWaitLockPerPacket); + QString averageUncompressPerPacketString = locale.toString(averageUncompressPerPacket); + QString averageReadBitstreamPerPacketString = locale.toString(averageReadBitstreamPerPacket); + + label = _labels[_processedPackets]; + statsValue.str(""); + statsValue << + "" << qPrintable(averagePacketsPerSecondString) << " per second"; + + label->setText(statsValue.str().c_str()); + + label = _labels[_processedPacketsElements]; + statsValue.str(""); + statsValue << + "" << qPrintable(averageElementsPerPacketString) << " per packet / " << + "" << qPrintable(averageElementsPerSecondString) << " per second"; + + label->setText(statsValue.str().c_str()); + + label = _labels[_processedPacketsEntities]; + statsValue.str(""); + statsValue << + "" << qPrintable(averageEntitiesPerPacketString) << " per packet / " << + "" << qPrintable(averageEntitiesPerSecondString) << " per second"; + + label->setText(statsValue.str().c_str()); + + label = _labels[_processedPacketsTiming]; + statsValue.str(""); + statsValue << + "Lock Wait:" << qPrintable(averageWaitLockPerPacketString) << " (usecs) / " << + "Uncompress:" << qPrintable(averageUncompressPerPacketString) << " (usecs) / " << + "Process:" << qPrintable(averageReadBitstreamPerPacketString) << " (usecs)"; + + label->setText(statsValue.str().c_str()); + + // Entity Edits update time + label = _labels[_entityUpdateTime]; + auto averageEditDelta = entitiesTree->getAverageEditDeltas(); + auto maxEditDelta = entitiesTree->getMaxEditDelta(); + + QString averageEditDeltaString = locale.toString((uint)averageEditDelta); + QString maxEditDeltaString = locale.toString((uint)maxEditDelta); + + statsValue.str(""); + statsValue << + "Average: " << qPrintable(averageEditDeltaString) << " (usecs) / " << + "Max: " << qPrintable(maxEditDeltaString) << " (usecs)"; + + label->setText(statsValue.str().c_str()); + + // Entity Edits + label = _labels[_entityUpdates]; + auto bytesPerEdit = entitiesTree->getAverageEditBytes(); + + auto updatesPerSecond = _averageUpdatesPerSecond.getAverage(); + if (updatesPerSecond < 1) { + updatesPerSecond = 0; // we don't really care about small updates per second so suppress those + } + + QString totalTrackedEditsString = locale.toString((uint)totalTrackedEdits); + QString updatesPerSecondString = locale.toString(updatesPerSecond); + QString bytesPerEditString = locale.toString(bytesPerEdit); + + statsValue.str(""); + statsValue << + "" << qPrintable(updatesPerSecondString) << " updates per second / " << + "" << qPrintable(totalTrackedEditsString) << " total updates / " << + "Average Size: " << qPrintable(bytesPerEditString) << " bytes "; + + label->setText(statsValue.str().c_str()); + showAllOctreeServers(); - this->QDialog::paintEvent(event); + QDialog::paintEvent(event); } void OctreeStatsDialog::showAllOctreeServers() { int serverCount = 0; diff --git a/interface/src/ui/OctreeStatsDialog.h b/interface/src/ui/OctreeStatsDialog.h index 03683d8171..063c04b295 100644 --- a/interface/src/ui/OctreeStatsDialog.h +++ b/interface/src/ui/OctreeStatsDialog.h @@ -63,6 +63,21 @@ private: int _serverElements; int _localElements; int _localElementsMemory; + + int _entityUpdateTime; + int _entityUpdates; + int _processedPackets; + int _processedPacketsElements; + int _processedPacketsEntities; + int _processedPacketsTiming; + + const int SAMPLES_PER_SECOND = 10; + SimpleMovingAverage _averageUpdatesPerSecond; + quint64 _lastWindowAt = 0; + quint64 _lastKnownTrackedEdits = 0; + + quint64 _lastRefresh = 0; + int _octreeServerLables[MAX_VOXEL_SERVERS]; int _octreeServerLabelsCount; details _extraServerDetails[MAX_VOXEL_SERVERS]; diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index 288a950bbb..988223765a 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -93,31 +93,6 @@ void BillboardOverlay::render(RenderArgs* args) { glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha)); batch->setUniformTexture(0, args->_whiteTexture); // restore default white color after me - } else { - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER, 0.5f); - - glEnable(GL_TEXTURE_2D); - glDisable(GL_LIGHTING); - - glBindTexture(GL_TEXTURE_2D, _texture->getID()); - - glPushMatrix(); { - glTranslatef(getPosition().x, getPosition().y, getPosition().z); - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - glScalef(_dimensions.x, _dimensions.y, 1.0f); - - DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, - glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha)); - - } glPopMatrix(); - - glDisable(GL_TEXTURE_2D); - glEnable(GL_LIGHTING); - glDisable(GL_ALPHA_TEST); - - glBindTexture(GL_TEXTURE_2D, 0); } } diff --git a/interface/src/ui/overlays/Grid3DOverlay.cpp b/interface/src/ui/overlays/Grid3DOverlay.cpp index 6316c8cd77..baf7b42ac5 100644 --- a/interface/src/ui/overlays/Grid3DOverlay.cpp +++ b/interface/src/ui/overlays/Grid3DOverlay.cpp @@ -89,75 +89,6 @@ void Grid3DOverlay::render(RenderArgs* args) { DependencyManager::get()->renderGrid(*batch, MAJOR_GRID_DIVISIONS, MAJOR_GRID_DIVISIONS, gridColor); } - } else { - if (!_gridProgram.isLinked()) { - if (!_gridProgram.addShaderFromSourceFile(QGLShader::Vertex, PathUtils::resourcesPath() + "shaders/grid.vert")) { - qDebug() << "Failed to compile: " + _gridProgram.log(); - return; - } - if (!_gridProgram.addShaderFromSourceFile(QGLShader::Fragment, PathUtils::resourcesPath() + "shaders/grid.frag")) { - qDebug() << "Failed to compile: " + _gridProgram.log(); - return; - } - if (!_gridProgram.link()) { - qDebug() << "Failed to link: " + _gridProgram.log(); - return; - } - } - - // Render code largely taken from MetavoxelEditor::render() - glDisable(GL_LIGHTING); - - glDepthMask(GL_FALSE); - - glPushMatrix(); - - glm::quat rotation = getRotation(); - - glm::vec3 axis = glm::axis(rotation); - - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - - glLineWidth(1.5f); - - glm::vec3 position = getPosition(); - - _gridProgram.bind(); - - // Minor grid - glPushMatrix(); - { - glTranslatef(_minorGridWidth * (floorf(rotated.x / spacing) - MINOR_GRID_DIVISIONS / 2), - spacing * (floorf(rotated.y / spacing) - MINOR_GRID_DIVISIONS / 2), position.z); - - float scale = MINOR_GRID_DIVISIONS * spacing; - glScalef(scale, scale, scale); - - DependencyManager::get()->renderGrid(MINOR_GRID_DIVISIONS, MINOR_GRID_DIVISIONS, gridColor); - } - glPopMatrix(); - - // Major grid - glPushMatrix(); - { - glLineWidth(4.0f); - spacing *= _majorGridEvery; - glTranslatef(spacing * (floorf(rotated.x / spacing) - MAJOR_GRID_DIVISIONS / 2), - spacing * (floorf(rotated.y / spacing) - MAJOR_GRID_DIVISIONS / 2), position.z); - - float scale = MAJOR_GRID_DIVISIONS * spacing; - glScalef(scale, scale, scale); - - DependencyManager::get()->renderGrid(MAJOR_GRID_DIVISIONS, MAJOR_GRID_DIVISIONS, gridColor); - } - glPopMatrix(); - - _gridProgram.release(); - - glPopMatrix(); - - glEnable(GL_LIGHTING); - glDepthMask(GL_TRUE); } } diff --git a/interface/src/ui/overlays/LocalModelsOverlay.cpp b/interface/src/ui/overlays/LocalModelsOverlay.cpp index 6853cbb70a..6e0b53f0c2 100644 --- a/interface/src/ui/overlays/LocalModelsOverlay.cpp +++ b/interface/src/ui/overlays/LocalModelsOverlay.cpp @@ -32,22 +32,22 @@ void LocalModelsOverlay::update(float deltatime) { void LocalModelsOverlay::render(RenderArgs* args) { if (_visible) { - float glowLevel = getGlowLevel(); Glower* glower = NULL; if (glowLevel > 0.0f) { glower = new Glower(glowLevel); } - - glPushMatrix(); { - Application* app = Application::getInstance(); -#if 0 - glm::vec3 oldTranslation = app->getViewMatrixTranslation(); - app->setViewMatrixTranslation(oldTranslation + getPosition()); - _entityTreeRenderer->render(args); - Application::getInstance()->setViewMatrixTranslation(oldTranslation); -#endif - } glPopMatrix(); + + auto batch = args ->_batch; + Application* app = Application::getInstance(); + glm::vec3 oldTranslation = app->getViewMatrixTranslation(); + Transform transform = Transform(); + transform.setTranslation(oldTranslation + getPosition()); + batch->setViewTransform(transform); + _entityTreeRenderer->render(args); + transform.setTranslation(oldTranslation); + batch->setViewTransform(transform); + if (glower) { delete glower; } diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index dc49478f38..fb0a095e13 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -16,6 +16,7 @@ #include #include +#include #include "BillboardOverlay.h" #include "Circle3DOverlay.h" @@ -96,6 +97,10 @@ void Overlays::cleanupOverlaysToDelete() { void Overlays::renderHUD(RenderArgs* renderArgs) { QReadLocker lock(&_lock); + gpu::Batch batch; + renderArgs->_batch = &batch; + + foreach(Overlay::Pointer thisOverlay, _overlaysHUD) { if (thisOverlay->is3D()) { glEnable(GL_DEPTH_TEST); @@ -109,6 +114,7 @@ void Overlays::renderHUD(RenderArgs* renderArgs) { thisOverlay->render(renderArgs); } } + gpu::GLBackend::renderBatch(batch, true); } unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& properties) { diff --git a/interface/src/ui/overlays/OverlaysPayload.cpp b/interface/src/ui/overlays/OverlaysPayload.cpp index b7cacef80c..e6b37d693b 100644 --- a/interface/src/ui/overlays/OverlaysPayload.cpp +++ b/interface/src/ui/overlays/OverlaysPayload.cpp @@ -62,19 +62,19 @@ namespace render { template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args) { if (args) { if (overlay->getAnchor() == Overlay::MY_AVATAR) { - glPushMatrix(); + auto batch = args->_batch; 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); + Transform transform = Transform(); + transform.setTranslation(myAvatarPosition); + transform.setRotation(glm::angleAxis(angle, axis)); + transform.setScale(myAvatarScale); + batch->setModelTransform(transform); overlay->render(args); - glPopMatrix(); } else { overlay->render(args); } diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.cpp b/interface/src/ui/overlays/Rectangle3DOverlay.cpp index 74bbd1bca8..ddab8040b1 100644 --- a/interface/src/ui/overlays/Rectangle3DOverlay.cpp +++ b/interface/src/ui/overlays/Rectangle3DOverlay.cpp @@ -87,70 +87,6 @@ void Rectangle3DOverlay::render(RenderArgs* args) { geometryCache->renderVertices(*batch, gpu::LINE_STRIP, _geometryCacheID); } } - } else { - glDisable(GL_LIGHTING); - - float glowLevel = getGlowLevel(); - Glower* glower = NULL; - if (glowLevel > 0.0f) { - glower = new Glower(glowLevel); - } - - 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, 1.0f); - - glLineWidth(_lineWidth); - - auto geometryCache = DependencyManager::get(); - - // for our overlay, is solid means we draw a solid "filled" rectangle otherwise we just draw a border line... - if (getIsSolid()) { - glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, 0.0f); - glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, 0.0f); - DependencyManager::get()->renderQuad(topLeft, bottomRight, rectangleColor); - } else { - if (getIsDashedLine()) { - - glm::vec3 point1(-halfDimensions.x, -halfDimensions.y, 0.0f); - glm::vec3 point2(halfDimensions.x, -halfDimensions.y, 0.0f); - glm::vec3 point3(halfDimensions.x, halfDimensions.y, 0.0f); - glm::vec3 point4(-halfDimensions.x, halfDimensions.y, 0.0f); - - geometryCache->renderDashedLine(point1, point2, rectangleColor); - geometryCache->renderDashedLine(point2, point3, rectangleColor); - geometryCache->renderDashedLine(point3, point4, rectangleColor); - geometryCache->renderDashedLine(point4, point1, rectangleColor); - - } else { - - if (halfDimensions != _previousHalfDimensions) { - QVector border; - border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f); - border << glm::vec3(halfDimensions.x, -halfDimensions.y, 0.0f); - border << glm::vec3(halfDimensions.x, halfDimensions.y, 0.0f); - border << glm::vec3(-halfDimensions.x, halfDimensions.y, 0.0f); - border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f); - geometryCache->updateVertices(_geometryCacheID, border, rectangleColor); - - _previousHalfDimensions = halfDimensions; - - } - geometryCache->renderVertices(gpu::LINE_STRIP, _geometryCacheID); - } - } - - glPopMatrix(); - glPopMatrix(); - - if (glower) { - delete glower; - } } } diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp index e129954db9..259fe98acf 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.cpp +++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp @@ -41,35 +41,6 @@ void Sphere3DOverlay::render(RenderArgs* args) { transform.postScale(getDimensions()); batch->setModelTransform(transform); DependencyManager::get()->renderSphere(*batch, 1.0f, SLICES, SLICES, sphereColor, _isSolid); - } else { - glDisable(GL_LIGHTING); - - glm::vec3 position = getPosition(); - glm::vec3 center = getCenter(); - glm::vec3 dimensions = getDimensions(); - glm::quat rotation = getRotation(); - - float glowLevel = getGlowLevel(); - Glower* glower = NULL; - if (glowLevel > 0.0f) { - glower = new Glower(glowLevel); - } - - 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()->renderSphere(1.0f, SLICES, SLICES, sphereColor, _isSolid); - glPopMatrix(); - glPopMatrix(); - - if (glower) { - delete glower; - } } } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 14a64d289e..ae1c97f07f 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -167,6 +167,25 @@ namespace render { } } +void makeEntityItemStatusGetters(RenderableModelEntityItem* entity, render::Item::Status::Getters& statusGetters) { + statusGetters.push_back([entity] () -> render::Item::Status::Value { + quint64 delta = usecTimestampNow() - entity->getLastEditedFromRemote(); + const float WAIT_THRESHOLD_INV = 1.0f / (0.2f * USECS_PER_SECOND); + float normalizedDelta = delta * WAIT_THRESHOLD_INV; + // Status icon will scale from 1.0f down to 0.0f after WAIT_THRESHOLD + // Color is red if last update is after WAIT_THRESHOLD, green otherwise (120 deg is green) + return render::Item::Status::Value(1.0f - normalizedDelta, (normalizedDelta > 1.0f ? render::Item::Status::Value::GREEN : render::Item::Status::Value::RED)); + }); + statusGetters.push_back([entity] () -> render::Item::Status::Value { + quint64 delta = usecTimestampNow() - entity->getLastBroadcast(); + const float WAIT_THRESHOLD_INV = 1.0f / (0.4f * USECS_PER_SECOND); + float normalizedDelta = delta * WAIT_THRESHOLD_INV; + // Status icon will scale from 1.0f down to 0.0f after WAIT_THRESHOLD + // Color is Magenta if last update is after WAIT_THRESHOLD, cyan otherwise (180 deg is green) + return render::Item::Status::Value(1.0f - normalizedDelta, (normalizedDelta > 1.0f ? render::Item::Status::Value::MAGENTA : render::Item::Status::Value::CYAN)); + }); +} + bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { _myMetaItem = scene->allocateID(); @@ -177,7 +196,9 @@ bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_p pendingChanges.resetItem(_myMetaItem, renderPayload); if (_model) { - return _model->addToScene(scene, pendingChanges); + render::Item::Status::Getters statusGetters; + makeEntityItemStatusGetters(this, statusGetters); + return _model->addToScene(scene, pendingChanges, statusGetters); } return true; @@ -214,7 +235,10 @@ void RenderableModelEntityItem::render(RenderArgs* args) { render::PendingChanges pendingChanges; if (_model->needsFixupInScene()) { _model->removeFromScene(scene, pendingChanges); - _model->addToScene(scene, pendingChanges); + + render::Item::Status::Getters statusGetters; + makeEntityItemStatusGetters(this, statusGetters); + _model->addToScene(scene, pendingChanges, statusGetters); } scene->enqueuePendingChanges(pendingChanges); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 6559289e33..6c27152a97 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -30,9 +30,6 @@ #include "EntityActionFactoryInterface.h" -const quint64 DEFAULT_SIMULATOR_CHANGE_LOCKOUT_PERIOD = (quint64)(0.2f * USECS_PER_SECOND); -const quint64 MAX_SIMULATOR_CHANGE_LOCKOUT_PERIOD = 2 * USECS_PER_SECOND; - bool EntityItem::_sendPhysicsUpdates = true; int EntityItem::_maxActionsDataSize = 800; @@ -346,6 +343,8 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef return 0; } + args.entitiesPerPacket++; + // Header bytes // object ID [16 bytes] // ByteCountCoded(type code) [~1 byte] @@ -426,6 +425,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef dataAt += sizeof(lastEditedFromBuffer); bytesRead += sizeof(lastEditedFromBuffer); lastEditedFromBufferAdjusted = lastEditedFromBuffer - clockSkew; + if (lastEditedFromBufferAdjusted > now) { lastEditedFromBufferAdjusted = now; } @@ -498,6 +498,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef qCDebug(entities) << " lastEditedFromBufferAdjusted:" << debugTime(lastEditedFromBufferAdjusted, now); #endif } + encodedUpdateDelta = updateDeltaCoder; // determine true length dataAt += encodedUpdateDelta.size(); bytesRead += encodedUpdateDelta.size(); @@ -676,6 +677,14 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef } } + // Tracking for editing roundtrips here. We will tell our EntityTree that we just got incoming data about + // and entity that was edited at some time in the past. The tree will determine how it wants to track this + // information. + if (_element && _element->getTree()) { + _element->getTree()->trackIncomingEntityLastEdited(lastEditedFromBufferAdjusted, bytesRead); + } + + return bytesRead; } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index a341051986..3d7fcd8ce5 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -26,15 +26,13 @@ #include "LogHandler.h" -const quint64 SIMULATOR_CHANGE_LOCKOUT_PERIOD = (quint64)(0.2f * USECS_PER_SECOND); - - EntityTree::EntityTree(bool shouldReaverage) : Octree(shouldReaverage), _fbxService(NULL), _simulation(NULL) { _rootElement = createNewElement(); + resetClientEditStats(); } EntityTree::~EntityTree() { @@ -61,6 +59,8 @@ void EntityTree::eraseAllOctreeElements(bool createNewRoot) { } _entityToElementMap.clear(); Octree::eraseAllOctreeElements(createNewRoot); + + resetClientEditStats(); } bool EntityTree::handlesEditPacketType(PacketType packetType) const { @@ -1123,3 +1123,29 @@ bool EntityTree::readFromMap(QVariantMap& map) { return true; } + +void EntityTree::resetClientEditStats() { + _treeResetTime = usecTimestampNow(); + _maxEditDelta = 0; + _totalEditDeltas = 0; + _totalTrackedEdits = 0; +} + + + +void EntityTree::trackIncomingEntityLastEdited(quint64 lastEditedTime, int bytesRead) { + // we don't want to track all edit deltas, just those edits that have happend + // since we connected to this domain. This will filter out all previously created + // content and only track new edits + if (lastEditedTime > _treeResetTime) { + quint64 now = usecTimestampNow(); + quint64 sinceEdit = now - lastEditedTime; + + _totalEditDeltas += sinceEdit; + _totalEditBytes += bytesRead; + _totalTrackedEdits++; + if (sinceEdit > _maxEditDelta) { + _maxEditDelta = sinceEdit; + } + } +} diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index fa72cc7691..263cff2171 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -185,6 +185,14 @@ public: virtual quint64 getAverageCreateTime() const { return _totalCreates == 0 ? 0 : _totalCreateTime / _totalCreates; } virtual quint64 getAverageLoggingTime() const { return _totalEditMessages == 0 ? 0 : _totalLoggingTime / _totalEditMessages; } + void trackIncomingEntityLastEdited(quint64 lastEditedTime, int bytesRead); + quint64 getAverageEditDeltas() const + { return _totalTrackedEdits == 0 ? 0 : _totalEditDeltas / _totalTrackedEdits; } + quint64 getAverageEditBytes() const + { return _totalTrackedEdits == 0 ? 0 : _totalEditBytes / _totalTrackedEdits; } + quint64 getMaxEditDelta() const { return _maxEditDelta; } + quint64 getTotalTrackedEdits() const { return _totalTrackedEdits; } + signals: void deletingEntity(const EntityItemID& entityID); void addingEntity(const EntityItemID& entityID); @@ -230,6 +238,14 @@ private: quint64 _totalUpdateTime = 0; quint64 _totalCreateTime = 0; quint64 _totalLoggingTime = 0; + + // these performance statistics are only used in the client + void resetClientEditStats(); + int _totalTrackedEdits = 0; + quint64 _totalEditBytes = 0; + quint64 _totalEditDeltas = 0; + quint64 _maxEditDelta = 0; + quint64 _treeResetTime = 0; }; #endif // hifi_EntityTree_h diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 4bc81e1da6..41c0529b80 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -702,6 +702,8 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int int bytesRead = 0; uint16_t numberOfEntities = 0; int expectedBytesPerEntity = EntityItem::expectedBytes(); + + args.elementsPerPacket++; if (bytesLeftToRead >= (int)sizeof(numberOfEntities)) { // read our entities in.... diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index bff99e7ec3..ee028e79e6 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -159,6 +159,12 @@ void Batch::setProjectionTransform(const Mat4& proj) { _params.push_back(cacheData(sizeof(Mat4), &proj)); } +void Batch::setViewportTransform(const Vec4i& viewport) { + ADD_COMMAND(setViewportTransform); + + _params.push_back(cacheData(sizeof(Vec4i), &viewport)); +} + void Batch::setPipeline(const PipelinePointer& pipeline) { ADD_COMMAND(setPipeline); diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 9c97db65ef..835e872b4a 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -105,6 +105,7 @@ public: void setModelTransform(const Transform& model); void setViewTransform(const Transform& view); void setProjectionTransform(const Mat4& proj); + void setViewportTransform(const Vec4i& viewport); // Viewport is xy = low left corner in the framebuffer, zw = width height of the viewport // Pipeline Stage void setPipeline(const PipelinePointer& pipeline); @@ -154,6 +155,7 @@ public: void _glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); void _glUniform3fv(GLint location, GLsizei count, const GLfloat* value); void _glUniform4fv(GLint location, GLsizei count, const GLfloat* value); + void _glUniform4iv(GLint location, GLsizei count, const GLint* value); void _glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); void _glEnableVertexAttribArray(GLint location); @@ -177,6 +179,7 @@ public: COMMAND_setModelTransform, COMMAND_setViewTransform, COMMAND_setProjectionTransform, + COMMAND_setViewportTransform, COMMAND_setPipeline, COMMAND_setStateBlendFactor, @@ -217,6 +220,7 @@ public: COMMAND_glUniform3f, COMMAND_glUniform3fv, COMMAND_glUniform4fv, + COMMAND_glUniform4iv, COMMAND_glUniformMatrix4fv, COMMAND_glEnableVertexAttribArray, diff --git a/libraries/gpu/src/gpu/Config.slh b/libraries/gpu/src/gpu/Config.slh index 28f447a696..76be161822 100644 --- a/libraries/gpu/src/gpu/Config.slh +++ b/libraries/gpu/src/gpu/Config.slh @@ -18,11 +18,13 @@ <@elif GLPROFILE == MAC_GL @> <@def GPU_FEATURE_PROFILE GPU_LEGACY@> <@def GPU_TRANSFORM_PROFILE GPU_LEGACY@> - <@def VERSION_HEADER #version 120@> + <@def VERSION_HEADER #version 120 +#extension GL_EXT_gpu_shader4 : enable@> <@else@> <@def GPU_FEATURE_PROFILE GPU_LEGACY@> <@def GPU_TRANSFORM_PROFILE GPU_LEGACY@> - <@def VERSION_HEADER #version 120@> + <@def VERSION_HEADER #version 120 +#extension GL_EXT_gpu_shader4 : enable@> <@endif@> <@endif@> diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index 7cf913430d..ac71cc7940 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -38,6 +38,7 @@ typedef uint32 Offset; typedef glm::mat4 Mat4; typedef glm::mat3 Mat3; typedef glm::vec4 Vec4; +typedef glm::ivec4 Vec4i; typedef glm::vec3 Vec3; typedef glm::vec2 Vec2; typedef glm::ivec2 Vec2i; diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index 9004c4a8fe..302dc0e8be 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -29,6 +29,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::GLBackend::do_setModelTransform), (&::gpu::GLBackend::do_setViewTransform), (&::gpu::GLBackend::do_setProjectionTransform), + (&::gpu::GLBackend::do_setViewportTransform), (&::gpu::GLBackend::do_setPipeline), (&::gpu::GLBackend::do_setStateBlendFactor), @@ -67,6 +68,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::GLBackend::do_glUniform3f), (&::gpu::GLBackend::do_glUniform3fv), (&::gpu::GLBackend::do_glUniform4fv), + (&::gpu::GLBackend::do_glUniform4iv), (&::gpu::GLBackend::do_glUniformMatrix4fv), (&::gpu::GLBackend::do_glEnableVertexAttribArray), @@ -190,6 +192,18 @@ void GLBackend::do_drawIndexed(Batch& batch, uint32 paramOffset) { } void GLBackend::do_drawInstanced(Batch& batch, uint32 paramOffset) { + updateInput(); + updateTransform(); + updatePipeline(); + + GLint numInstances = batch._params[paramOffset + 4]._uint; + Primitive primitiveType = (Primitive)batch._params[paramOffset + 3]._uint; + GLenum mode = _primitiveToGLmode[primitiveType]; + uint32 numVertices = batch._params[paramOffset + 2]._uint; + uint32 startVertex = batch._params[paramOffset + 1]._uint; + uint32 startInstance = batch._params[paramOffset + 0]._uint; + + glDrawArraysInstancedARB(mode, startVertex, numVertices, numInstances); (void) CHECK_GL_ERROR(); } @@ -592,6 +606,31 @@ void GLBackend::do_glUniform4fv(Batch& batch, uint32 paramOffset) { (void) CHECK_GL_ERROR(); } +void Batch::_glUniform4iv(GLint location, GLsizei count, const GLint* value) { + ADD_COMMAND_GL(glUniform4iv); + + const int VEC4_SIZE = 4 * sizeof(int); + _params.push_back(cacheData(count * VEC4_SIZE, value)); + _params.push_back(count); + _params.push_back(location); + + DO_IT_NOW(_glUniform4iv, 3); +} +void GLBackend::do_glUniform4iv(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; + } + updatePipeline(); + glUniform4iv( + batch._params[paramOffset + 2]._int, + batch._params[paramOffset + 1]._uint, + (const GLint*)batch.editData(batch._params[paramOffset + 0]._uint)); + + (void) CHECK_GL_ERROR(); +} + void Batch::_glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) { ADD_COMMAND_GL(glUniformMatrix4fv); diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index 28236c68c9..d798e9aaac 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -96,7 +96,9 @@ public: #if (GPU_TRANSFORM_PROFILE == GPU_CORE) #else + GLuint _transformObject_model = -1; GLuint _transformCamera_viewInverse = -1; + GLuint _transformCamera_viewport = -1; #endif GLShader(); @@ -267,7 +269,8 @@ protected: void do_setModelTransform(Batch& batch, uint32 paramOffset); void do_setViewTransform(Batch& batch, uint32 paramOffset); void do_setProjectionTransform(Batch& batch, uint32 paramOffset); - + void do_setViewportTransform(Batch& batch, uint32 paramOffset); + void initTransform(); void killTransform(); // Synchronize the state cache of this Backend with the actual real state of the GL Context @@ -281,9 +284,11 @@ protected: Transform _model; Transform _view; Mat4 _projection; + Vec4i _viewport; bool _invalidModel; bool _invalidView; bool _invalidProj; + bool _invalidViewport; GLenum _lastMode; @@ -296,6 +301,7 @@ protected: _invalidModel(true), _invalidView(true), _invalidProj(false), + _invalidViewport(false), _lastMode(GL_TEXTURE) {} } _transform; @@ -329,7 +335,9 @@ protected: #if (GPU_TRANSFORM_PROFILE == GPU_CORE) #else + GLint _program_transformObject_model = -1; GLint _program_transformCamera_viewInverse = -1; + GLint _program_transformCamera_viewport = -1; #endif State::Data _stateCache; @@ -390,6 +398,7 @@ protected: void do_glUniform3f(Batch& batch, uint32 paramOffset); void do_glUniform3fv(Batch& batch, uint32 paramOffset); void do_glUniform4fv(Batch& batch, uint32 paramOffset); + void do_glUniform4iv(Batch& batch, uint32 paramOffset); void do_glUniformMatrix4fv(Batch& batch, uint32 paramOffset); void do_glEnableVertexAttribArray(Batch& batch, uint32 paramOffset); diff --git a/libraries/gpu/src/gpu/GLBackendPipeline.cpp b/libraries/gpu/src/gpu/GLBackendPipeline.cpp index 06d9eadd87..a770cf89b3 100755 --- a/libraries/gpu/src/gpu/GLBackendPipeline.cpp +++ b/libraries/gpu/src/gpu/GLBackendPipeline.cpp @@ -73,7 +73,9 @@ void GLBackend::do_setPipeline(Batch& batch, uint32 paramOffset) { #if (GPU_TRANSFORM_PROFILE == GPU_CORE) #else + _pipeline._program_transformObject_model = -1; _pipeline._program_transformCamera_viewInverse = -1; + _pipeline._program_transformCamera_viewport = -1; #endif _pipeline._state = nullptr; @@ -91,7 +93,9 @@ void GLBackend::do_setPipeline(Batch& batch, uint32 paramOffset) { #if (GPU_TRANSFORM_PROFILE == GPU_CORE) #else + _pipeline._program_transformObject_model = pipelineObject->_program->_transformObject_model; _pipeline._program_transformCamera_viewInverse = pipelineObject->_program->_transformCamera_viewInverse; + _pipeline._program_transformCamera_viewport = pipelineObject->_program->_transformCamera_viewport; #endif } @@ -143,10 +147,20 @@ void GLBackend::updatePipeline() { #if (GPU_TRANSFORM_PROFILE == GPU_CORE) #else + // If shader program needs the model we need to provide it + if (_pipeline._program_transformObject_model >= 0) { + glUniformMatrix4fv(_pipeline._program_transformObject_model, 1, false, (const GLfloat*) &_transform._transformObject._model); + } + // 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); } + + // If shader program needs the viewport we need to provide it + if (_pipeline._program_transformCamera_viewport >= 0) { + glUniform4fv(_pipeline._program_transformCamera_viewport, 1, (const GLfloat*) &_transform._transformCamera._viewport); + } #endif } diff --git a/libraries/gpu/src/gpu/GLBackendShader.cpp b/libraries/gpu/src/gpu/GLBackendShader.cpp index 45adbcdb3c..ec02c1333b 100755 --- a/libraries/gpu/src/gpu/GLBackendShader.cpp +++ b/libraries/gpu/src/gpu/GLBackendShader.cpp @@ -111,10 +111,20 @@ void makeBindings(GLBackend::GLShader* shader) { shader->_transformCameraSlot = gpu::TRANSFORM_CAMERA_SLOT; } #else + loc = glGetUniformLocation(glprogram, "transformObject_model"); + if (loc >= 0) { + shader->_transformObject_model = loc; + } + loc = glGetUniformLocation(glprogram, "transformCamera_viewInverse"); if (loc >= 0) { shader->_transformCamera_viewInverse = loc; } + + loc = glGetUniformLocation(glprogram, "transformCamera_viewport"); + if (loc >= 0) { + shader->_transformCamera_viewport = loc; + } #endif } diff --git a/libraries/gpu/src/gpu/GLBackendTransform.cpp b/libraries/gpu/src/gpu/GLBackendTransform.cpp index 2e3c2dca70..21a2d57271 100755 --- a/libraries/gpu/src/gpu/GLBackendTransform.cpp +++ b/libraries/gpu/src/gpu/GLBackendTransform.cpp @@ -31,6 +31,12 @@ void GLBackend::do_setProjectionTransform(Batch& batch, uint32 paramOffset) { _transform._invalidProj = true; } +void GLBackend::do_setViewportTransform(Batch& batch, uint32 paramOffset) { + memcpy(&_transform._viewport, batch.editData(batch._params[paramOffset]._uint), sizeof(Vec4i)); + _transform._invalidViewport = true; +} + + void GLBackend::initTransform() { #if (GPU_TRANSFORM_PROFILE == GPU_CORE) glGenBuffers(1, &_transform._transformObjectBuffer); @@ -57,10 +63,13 @@ void GLBackend::killTransform() { } void GLBackend::syncTransformStateCache() { + _transform._invalidViewport = true; _transform._invalidProj = true; _transform._invalidView = true; _transform._invalidModel = true; + glGetIntegerv(GL_VIEWPORT, (GLint*) &_transform._viewport); + GLint currentMode; glGetIntegerv(GL_MATRIX_MODE, ¤tMode); _transform._lastMode = currentMode; @@ -78,6 +87,13 @@ void GLBackend::updateTransform() { GLint originalMatrixMode; glGetIntegerv(GL_MATRIX_MODE, &originalMatrixMode); // Check all the dirty flags and update the state accordingly + if (_transform._invalidViewport) { + _transform._transformCamera._viewport = glm::vec4(_transform._viewport); + + // Where we assign the GL viewport + glViewport(_transform._viewport.x, _transform._viewport.y, _transform._viewport.z, _transform._viewport.w); + } + if (_transform._invalidProj) { _transform._transformCamera._projection = _transform._projection; _transform._transformCamera._projectionInverse = glm::inverse(_transform._projection); @@ -100,7 +116,7 @@ void GLBackend::updateTransform() { } #if (GPU_TRANSFORM_PROFILE == GPU_CORE) - if (_transform._invalidView || _transform._invalidProj) { + if (_transform._invalidView || _transform._invalidProj || _transform._invalidViewport) { glBindBufferBase(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, 0); glBindBuffer(GL_ARRAY_BUFFER, _transform._transformCameraBuffer); glBufferData(GL_ARRAY_BUFFER, sizeof(_transform._transformCamera), (const void*) &_transform._transformCamera, GL_DYNAMIC_DRAW); @@ -162,7 +178,8 @@ void GLBackend::updateTransform() { #endif // Flags are clean - _transform._invalidView = _transform._invalidProj = _transform._invalidModel = false; + _transform._invalidView = _transform._invalidProj = _transform._invalidModel = _transform._invalidViewport = false; + glMatrixMode(originalMatrixMode); } diff --git a/libraries/gpu/src/gpu/Transform.slh b/libraries/gpu/src/gpu/Transform.slh index d01fe128ae..274032a642 100644 --- a/libraries/gpu/src/gpu/Transform.slh +++ b/libraries/gpu/src/gpu/Transform.slh @@ -87,10 +87,18 @@ TransformCamera getTransformCamera() { } uniform mat4 transformCamera_viewInverse; +uniform vec4 transformCamera_viewport; <@endif@> <@endfunc@> +<@func transformCameraViewport(cameraTransform, viewport)@> +<@if GPU_TRANSFORM_PROFILE == GPU_CORE@> + <$viewport$> = <$cameraTransform$>._viewport; +<@else@> + <$viewport$> = transformCamera_viewport; +<@endif@> +<@endfunc@> <@func transformModelToClipPos(cameraTransform, objectTransform, modelPos, clipPos)@> <@if GPU_TRANSFORM_PROFILE == GPU_CORE@> diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index adc2040b8a..7f421368d8 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -667,11 +667,6 @@ OctreeElement* Octree::getOctreeElementAt(float x, float y, float z, float s) co element = NULL; } delete[] octalCode; // cleanup memory -#ifdef HAS_AUDIT_CHILDREN - if (element) { - element->auditChildren("Octree::getOctreeElementAt()"); - } -#endif // def HAS_AUDIT_CHILDREN return element; } @@ -680,11 +675,6 @@ OctreeElement* Octree::getOctreeEnclosingElementAt(float x, float y, float z, fl OctreeElement* element = nodeForOctalCode(_rootElement, octalCode, NULL); delete[] octalCode; // cleanup memory -#ifdef HAS_AUDIT_CHILDREN - if (element) { - element->auditChildren("Octree::getOctreeElementAt()"); - } -#endif // def HAS_AUDIT_CHILDREN return element; } diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 6eeb423ddd..f1d32aa390 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -195,6 +195,8 @@ public: SharedNodePointer sourceNode; bool wantImportProgress; PacketVersion bitstreamVersion; + int elementsPerPacket = 0; + int entitiesPerPacket = 0; ReadBitstreamToTreeParams( bool includeColor = WANT_COLOR, diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index e09d5fb910..e11a11fc8e 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -71,19 +71,9 @@ void OctreeElement::init(unsigned char * octalCode) { _childrenExternal = false; -#ifdef BLENDED_UNION_CHILDREN - _children.external = NULL; - _singleChildrenCount++; -#endif _childrenCount[0]++; // default pointers to child nodes to NULL -#ifdef HAS_AUDIT_CHILDREN - for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - _childrenArray[i] = NULL; - } -#endif // def HAS_AUDIT_CHILDREN - #ifdef SIMPLE_CHILD_ARRAY for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { _simpleChildArray[i] = NULL; @@ -218,9 +208,6 @@ void OctreeElement::deleteChildAtIndex(int childIndex) { _voxelNodeLeafCount++; } } -#ifdef HAS_AUDIT_CHILDREN - auditChildren("deleteChildAtIndex()"); -#endif // def HAS_AUDIT_CHILDREN } // does not delete the node! @@ -236,10 +223,6 @@ OctreeElement* OctreeElement::removeChildAtIndex(int childIndex) { _voxelNodeLeafCount++; } } - -#ifdef HAS_AUDIT_CHILDREN - auditChildren("removeChildAtIndex()"); -#endif // def HAS_AUDIT_CHILDREN return returnedChild; } @@ -255,60 +238,11 @@ bool OctreeElement::isParentOf(OctreeElement* possibleChild) const { return false; } - -#ifdef HAS_AUDIT_CHILDREN -void OctreeElement::auditChildren(const char* label) const { - bool auditFailed = false; - for (int childIndex = 0; childIndex < NUMBER_OF_CHILDREN; childIndex++) { - OctreeElement* testChildNew = getChildAtIndex(childIndex); - OctreeElement* testChildOld = _childrenArray[childIndex]; - - if (testChildNew != testChildOld) { - auditFailed = true; - } - } - - const bool alwaysReport = false; // set this to true to get additional debugging - if (alwaysReport || auditFailed) { - qDebug("%s... auditChildren() %s <<<<", label, (auditFailed ? "FAILED" : "PASSED")); - qDebug(" _childrenExternal=%s", debug::valueOf(_childrenExternal)); - qDebug(" childCount=%d", getChildCount()); - - QDebug bitOutput = qDebug().nospace(); - bitOutput << " _childBitmask="; - outputBits(_childBitmask, bitOutput); - - - for (int childIndex = 0; childIndex < NUMBER_OF_CHILDREN; childIndex++) { - OctreeElement* testChildNew = getChildAtIndex(childIndex); - OctreeElement* testChildOld = _childrenArray[childIndex]; - - qCebug("child at index %d... testChildOld=%p testChildNew=%p %s", - childIndex, testChildOld, testChildNew , - ((testChildNew != testChildOld) ? " DOES NOT MATCH <<<< BAD <<<<" : " - OK ") - ); - } - qDebug("%s... auditChildren() <<<< DONE <<<<", label); - } -} -#endif // def HAS_AUDIT_CHILDREN - - quint64 OctreeElement::_getChildAtIndexTime = 0; quint64 OctreeElement::_getChildAtIndexCalls = 0; quint64 OctreeElement::_setChildAtIndexTime = 0; quint64 OctreeElement::_setChildAtIndexCalls = 0; -#ifdef BLENDED_UNION_CHILDREN -quint64 OctreeElement::_singleChildrenCount = 0; -quint64 OctreeElement::_twoChildrenOffsetCount = 0; -quint64 OctreeElement::_twoChildrenExternalCount = 0; -quint64 OctreeElement::_threeChildrenOffsetCount = 0; -quint64 OctreeElement::_threeChildrenExternalCount = 0; -quint64 OctreeElement::_couldStoreFourChildrenInternally = 0; -quint64 OctreeElement::_couldNotStoreFourChildrenInternally = 0; -#endif - quint64 OctreeElement::_externalChildrenCount = 0; quint64 OctreeElement::_childrenCount[NUMBER_OF_CHILDREN + 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -341,319 +275,8 @@ OctreeElement* OctreeElement::getChildAtIndex(int childIndex) const { } break; } #endif // def SIMPLE_EXTERNAL_CHILDREN - -#ifdef BLENDED_UNION_CHILDREN - PerformanceWarning warn(false,"getChildAtIndex",false,&_getChildAtIndexTime,&_getChildAtIndexCalls); - OctreeElement* result = NULL; - int childCount = getChildCount(); - -#ifdef HAS_AUDIT_CHILDREN - const char* caseStr = NULL; -#endif - - switch (childCount) { - case 0: -#ifdef HAS_AUDIT_CHILDREN - caseStr = "0 child case"; -#endif - break; - case 1: { -#ifdef HAS_AUDIT_CHILDREN - caseStr = "1 child case"; -#endif - int indexOne = getNthBit(_childBitmask, 1); - if (indexOne == childIndex) { - result = _children.single; - } - } break; - case 2: { -#ifdef HAS_AUDIT_CHILDREN - caseStr = "2 child case"; -#endif - int indexOne = getNthBit(_childBitmask, 1); - int indexTwo = getNthBit(_childBitmask, 2); - - if (_childrenExternal) { - //assert(_children.external); - if (indexOne == childIndex) { - result = _children.external[0]; - } else if (indexTwo == childIndex) { - result = _children.external[1]; - } - } else { - if (indexOne == childIndex) { - int32_t offset = _children.offsetsTwoChildren[0]; - result = (OctreeElement*)((uint8_t*)this + offset); - } else if (indexTwo == childIndex) { - int32_t offset = _children.offsetsTwoChildren[1]; - result = (OctreeElement*)((uint8_t*)this + offset); - } - } - } break; - case 3: { -#ifdef HAS_AUDIT_CHILDREN - caseStr = "3 child case"; -#endif - int indexOne = getNthBit(_childBitmask, 1); - int indexTwo = getNthBit(_childBitmask, 2); - int indexThree = getNthBit(_childBitmask, 3); - - if (_childrenExternal) { - //assert(_children.external); - if (indexOne == childIndex) { - result = _children.external[0]; - } else if (indexTwo == childIndex) { - result = _children.external[1]; - } else if (indexThree == childIndex) { - result = _children.external[2]; - } else { - } - } else { - int64_t offsetOne, offsetTwo, offsetThree; - decodeThreeOffsets(offsetOne, offsetTwo, offsetThree); - - if (indexOne == childIndex) { - result = (OctreeElement*)((uint8_t*)this + offsetOne); - } else if (indexTwo == childIndex) { - result = (OctreeElement*)((uint8_t*)this + offsetTwo); - } else if (indexThree == childIndex) { - result = (OctreeElement*)((uint8_t*)this + offsetThree); - } - } - } break; - default: { -#ifdef HAS_AUDIT_CHILDREN - caseStr = "default"; -#endif - // if we have 4 or more, we know we're in external mode, so we just need to figure out which - // slot in our external array this child is. - if (oneAtBit(_childBitmask, childIndex)) { - childCount = getChildCount(); - for (int ordinal = 1; ordinal <= childCount; ordinal++) { - int index = getNthBit(_childBitmask, ordinal); - if (index == childIndex) { - int externalIndex = ordinal-1; - if (externalIndex < childCount && externalIndex >= 0) { - result = _children.external[externalIndex]; - } else { - qCDebug(octree, "getChildAtIndex() attempt to access external client out of " - "bounds externalIndex=%d <<<<<<<<<< WARNING!!!", externalIndex); - } - break; - } - } - } - } break; - } -#ifdef HAS_AUDIT_CHILDREN - if (result != _childrenArray[childIndex]) { - qCDebug(octree, "getChildAtIndex() case:%s result<%p> != _childrenArray[childIndex]<%p> <<<<<<<<<< WARNING!!!", - caseStr, result,_childrenArray[childIndex]); - } -#endif // def HAS_AUDIT_CHILDREN - return result; -#endif } -#ifdef BLENDED_UNION_CHILDREN -void OctreeElement::storeTwoChildren(OctreeElement* childOne, OctreeElement* childTwo) { - int64_t offsetOne = (uint8_t*)childOne - (uint8_t*)this; - int64_t offsetTwo = (uint8_t*)childTwo - (uint8_t*)this; - - const int64_t minOffset = std::numeric_limits::min(); - const int64_t maxOffset = std::numeric_limits::max(); - - bool forceExternal = true; - if (!forceExternal && isBetween(offsetOne, maxOffset, minOffset) && isBetween(offsetTwo, maxOffset, minOffset)) { - // if previously external, then clean it up... - if (_childrenExternal) { - //assert(_children.external); - const int previousChildCount = 2; - _externalChildrenMemoryUsage -= previousChildCount * sizeof(OctreeElement*); - delete[] _children.external; - _children.external = NULL; // probably not needed! - _childrenExternal = false; - } - - // encode in union - _children.offsetsTwoChildren[0] = offsetOne; - _children.offsetsTwoChildren[1] = offsetTwo; - - _twoChildrenOffsetCount++; - } else { - // encode in array - - // if not previously external, then allocate appropriately - if (!_childrenExternal) { - _childrenExternal = true; - const int newChildCount = 2; - _externalChildrenMemoryUsage += newChildCount * sizeof(OctreeElement*); - _children.external = new OctreeElement*[newChildCount]; - memset(_children.external, 0, sizeof(OctreeElement*) * newChildCount); - } - _children.external[0] = childOne; - _children.external[1] = childTwo; - _twoChildrenExternalCount++; - } -} - -void OctreeElement::retrieveTwoChildren(OctreeElement*& childOne, OctreeElement*& childTwo) { - // If we previously had an external array, then get the - if (_childrenExternal) { - childOne = _children.external[0]; - childTwo = _children.external[1]; - delete[] _children.external; - _children.external = NULL; // probably not needed! - _childrenExternal = false; - _twoChildrenExternalCount--; - const int newChildCount = 2; - _externalChildrenMemoryUsage -= newChildCount * sizeof(OctreeElement*); - } else { - int64_t offsetOne = _children.offsetsTwoChildren[0]; - int64_t offsetTwo = _children.offsetsTwoChildren[1]; - childOne = (OctreeElement*)((uint8_t*)this + offsetOne); - childTwo = (OctreeElement*)((uint8_t*)this + offsetTwo); - _twoChildrenOffsetCount--; - } -} - -void OctreeElement::decodeThreeOffsets(int64_t& offsetOne, int64_t& offsetTwo, int64_t& offsetThree) const { - const quint64 ENCODE_BITS = 21; - const quint64 ENCODE_MASK = 0xFFFFF; - const quint64 ENCODE_MASK_SIGN = 0x100000; - - quint64 offsetEncodedOne = (_children.offsetsThreeChildrenEncoded >> (ENCODE_BITS * 2)) & ENCODE_MASK; - quint64 offsetEncodedTwo = (_children.offsetsThreeChildrenEncoded >> (ENCODE_BITS * 1)) & ENCODE_MASK; - quint64 offsetEncodedThree = (_children.offsetsThreeChildrenEncoded & ENCODE_MASK); - - quint64 signEncodedOne = (_children.offsetsThreeChildrenEncoded >> (ENCODE_BITS * 2)) & ENCODE_MASK_SIGN; - quint64 signEncodedTwo = (_children.offsetsThreeChildrenEncoded >> (ENCODE_BITS * 1)) & ENCODE_MASK_SIGN; - quint64 signEncodedThree = (_children.offsetsThreeChildrenEncoded & ENCODE_MASK_SIGN); - - bool oneNegative = signEncodedOne == ENCODE_MASK_SIGN; - bool twoNegative = signEncodedTwo == ENCODE_MASK_SIGN; - bool threeNegative = signEncodedThree == ENCODE_MASK_SIGN; - - offsetOne = oneNegative ? -offsetEncodedOne : offsetEncodedOne; - offsetTwo = twoNegative ? -offsetEncodedTwo : offsetEncodedTwo; - offsetThree = threeNegative ? -offsetEncodedThree : offsetEncodedThree; -} - -void OctreeElement::encodeThreeOffsets(int64_t offsetOne, int64_t offsetTwo, int64_t offsetThree) { - const quint64 ENCODE_BITS = 21; - const quint64 ENCODE_MASK = 0xFFFFF; - const quint64 ENCODE_MASK_SIGN = 0x100000; - - quint64 offsetEncodedOne, offsetEncodedTwo, offsetEncodedThree; - if (offsetOne < 0) { - offsetEncodedOne = ((-offsetOne & ENCODE_MASK) | ENCODE_MASK_SIGN); - } else { - offsetEncodedOne = offsetOne & ENCODE_MASK; - } - offsetEncodedOne = offsetEncodedOne << (ENCODE_BITS * 2); - - if (offsetTwo < 0) { - offsetEncodedTwo = ((-offsetTwo & ENCODE_MASK) | ENCODE_MASK_SIGN); - } else { - offsetEncodedTwo = offsetTwo & ENCODE_MASK; - } - offsetEncodedTwo = offsetEncodedTwo << ENCODE_BITS; - - if (offsetThree < 0) { - offsetEncodedThree = ((-offsetThree & ENCODE_MASK) | ENCODE_MASK_SIGN); - } else { - offsetEncodedThree = offsetThree & ENCODE_MASK; - } - _children.offsetsThreeChildrenEncoded = offsetEncodedOne | offsetEncodedTwo | offsetEncodedThree; -} - -void OctreeElement::storeThreeChildren(OctreeElement* childOne, OctreeElement* childTwo, OctreeElement* childThree) { - int64_t offsetOne = (uint8_t*)childOne - (uint8_t*)this; - int64_t offsetTwo = (uint8_t*)childTwo - (uint8_t*)this; - int64_t offsetThree = (uint8_t*)childThree - (uint8_t*)this; - - const int64_t minOffset = -1048576; // what can fit in 20 bits // std::numeric_limits::min(); - const int64_t maxOffset = 1048576; // what can fit in 20 bits // std::numeric_limits::max(); - - bool forceExternal = true; - if (!forceExternal && - isBetween(offsetOne, maxOffset, minOffset) && - isBetween(offsetTwo, maxOffset, minOffset) && - isBetween(offsetThree, maxOffset, minOffset)) { - // if previously external, then clean it up... - if (_childrenExternal) { - delete[] _children.external; - _children.external = NULL; // probably not needed! - _childrenExternal = false; - const int previousChildCount = 3; - _externalChildrenMemoryUsage -= previousChildCount * sizeof(OctreeElement*); - } - // encode in union - encodeThreeOffsets(offsetOne, offsetTwo, offsetThree); - _threeChildrenOffsetCount++; - } else { - // encode in array - - // if not previously external, then allocate appropriately - if (!_childrenExternal) { - _childrenExternal = true; - const int newChildCount = 3; - _externalChildrenMemoryUsage += newChildCount * sizeof(OctreeElement*); - _children.external = new OctreeElement*[newChildCount]; - memset(_children.external, 0, sizeof(OctreeElement*) * newChildCount); - } - _children.external[0] = childOne; - _children.external[1] = childTwo; - _children.external[2] = childThree; - _threeChildrenExternalCount++; - } -} - -void OctreeElement::retrieveThreeChildren(OctreeElement*& childOne, OctreeElement*& childTwo, OctreeElement*& childThree) { - // If we previously had an external array, then get the - if (_childrenExternal) { - childOne = _children.external[0]; - childTwo = _children.external[1]; - childThree = _children.external[2]; - delete[] _children.external; - _children.external = NULL; // probably not needed! - _childrenExternal = false; - _threeChildrenExternalCount--; - _externalChildrenMemoryUsage -= 3 * sizeof(OctreeElement*); - } else { - int64_t offsetOne, offsetTwo, offsetThree; - decodeThreeOffsets(offsetOne, offsetTwo, offsetThree); - - childOne = (OctreeElement*)((uint8_t*)this + offsetOne); - childTwo = (OctreeElement*)((uint8_t*)this + offsetTwo); - childThree = (OctreeElement*)((uint8_t*)this + offsetThree); - _threeChildrenOffsetCount--; - } -} - -void OctreeElement::checkStoreFourChildren(OctreeElement* childOne, OctreeElement* childTwo, OctreeElement* childThree, OctreeElement* childFour) { - int64_t offsetOne = (uint8_t*)childOne - (uint8_t*)this; - int64_t offsetTwo = (uint8_t*)childTwo - (uint8_t*)this; - int64_t offsetThree = (uint8_t*)childThree - (uint8_t*)this; - int64_t offsetFour = (uint8_t*)childFour - (uint8_t*)this; - - const int64_t minOffset = std::numeric_limits::min(); - const int64_t maxOffset = std::numeric_limits::max(); - - bool forceExternal = true; - if (!forceExternal && - isBetween(offsetOne, maxOffset, minOffset) && - isBetween(offsetTwo, maxOffset, minOffset) && - isBetween(offsetThree, maxOffset, minOffset) && - isBetween(offsetFour, maxOffset, minOffset) - ) { - _couldStoreFourChildrenInternally++; - } else { - _couldNotStoreFourChildrenInternally++; - } -} -#endif - void OctreeElement::deleteAllChildren() { // first delete all the OctreeElement objects... for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { @@ -667,52 +290,6 @@ void OctreeElement::deleteAllChildren() { // if the children_t union represents _children.external we need to delete it here delete[] _children.external; } - -#ifdef BLENDED_UNION_CHILDREN - // now, reset our internal state and ANY and all population data - int childCount = getChildCount(); - switch (childCount) { - case 0: { - _singleChildrenCount--; - _childrenCount[0]--; - } break; - case 1: { - _singleChildrenCount--; - _childrenCount[1]--; - } break; - - case 2: { - if (_childrenExternal) { - _twoChildrenExternalCount--; - } else { - _twoChildrenOffsetCount--; - } - _childrenCount[2]--; - } break; - - case 3: { - if (_childrenExternal) { - _threeChildrenExternalCount--; - } else { - _threeChildrenOffsetCount--; - } - _childrenCount[3]--; - } break; - - default: { - _externalChildrenCount--; - _childrenCount[childCount]--; - } break; - - - } - - // If we had externally stored children, clean them too. - if (_childrenExternal && _children.external) { - delete[] _children.external; - } - _children.single = NULL; -#endif // BLENDED_UNION_CHILDREN } void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { @@ -788,353 +365,6 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { } #endif // def SIMPLE_EXTERNAL_CHILDREN - -#ifdef BLENDED_UNION_CHILDREN - PerformanceWarning warn(false,"setChildAtIndex",false,&_setChildAtIndexTime,&_setChildAtIndexCalls); - - // Here's how we store things... - // If we have 0 or 1 children, then we just store them in the _children.single; - // If we have 2 children, - // then if we can we store them as 32 bit signed offsets from our own this pointer, - // _children.offsetsTwoChildren[0]-[1] - // these are 32 bit offsets - - unsigned char previousChildMask = _childBitmask; - int previousChildCount = getChildCount(); - if (child) { - setAtBit(_childBitmask, childIndex); - } else { - clearAtBit(_childBitmask, childIndex); - } - int newChildCount = getChildCount(); - - // track our population data - if (previousChildCount != newChildCount) { - _childrenCount[previousChildCount]--; - _childrenCount[newChildCount]++; - } - - // If we had 0 children and we still have 0 children, then there is nothing to do. - if (previousChildCount == 0 && newChildCount == 0) { - // nothing to do... - } else if ((previousChildCount == 0 || previousChildCount == 1) && newChildCount == 1) { - // If we had 0 children, and we're setting our first child or if we had 1 child, or we're resetting the same child, - // then we can just store it in _children.single - _children.single = child; - } else if (previousChildCount == 1 && newChildCount == 0) { - // If we had 1 child, and we've removed our last child, then we can just store NULL in _children.single - _children.single = NULL; - } else if (previousChildCount == 1 && newChildCount == 2) { - // If we had 1 child, and we're adding a second child, then we need to determine - // if we can use offsets to store them - - OctreeElement* childOne; - OctreeElement* childTwo; - - if (getNthBit(previousChildMask, 1) < childIndex) { - childOne = _children.single; - childTwo = child; - } else { - childOne = child; - childTwo = _children.single; - } - - _singleChildrenCount--; - storeTwoChildren(childOne, childTwo); - } else if (previousChildCount == 2 && newChildCount == 1) { - // If we had 2 children, and we're removing one, then we know we can go down to single mode - //assert(child == NULL); // this is the only logical case - - int indexTwo = getNthBit(previousChildMask, 2); - bool keepChildOne = indexTwo == childIndex; - - OctreeElement* childOne; - OctreeElement* childTwo; - - retrieveTwoChildren(childOne, childTwo); - - _singleChildrenCount++; - - if (keepChildOne) { - _children.single = childOne; - } else { - _children.single = childTwo; - } - } else if (previousChildCount == 2 && newChildCount == 2) { - // If we had 2 children, and still have 2, then we know we are resetting one of our existing children - - int indexOne = getNthBit(previousChildMask, 1); - bool replaceChildOne = indexOne == childIndex; - - // Get the existing two children out of their encoding... - OctreeElement* childOne; - OctreeElement* childTwo; - retrieveTwoChildren(childOne, childTwo); - - if (replaceChildOne) { - childOne = child; - } else { - childTwo = child; - } - - storeTwoChildren(childOne, childTwo); - - } else if (previousChildCount == 2 && newChildCount == 3) { - // If we had 2 children, and now have 3, then we know we are going to an external case... - - // First, decode the children... - OctreeElement* childOne; - OctreeElement* childTwo; - OctreeElement* childThree; - - // Get the existing two children out of their encoding... - retrieveTwoChildren(childOne, childTwo); - - // determine order of the existing children - int indexOne = getNthBit(previousChildMask, 1); - int indexTwo = getNthBit(previousChildMask, 2); - - if (childIndex < indexOne) { - childThree = childTwo; - childTwo = childOne; - childOne = child; - } else if (childIndex < indexTwo) { - childThree = childTwo; - childTwo = child; - } else { - childThree = child; - } - storeThreeChildren(childOne, childTwo, childThree); - } else if (previousChildCount == 3 && newChildCount == 2) { - // If we had 3 children, and now have 2, then we know we are going from an external case to a potential internal case - - // We need to determine which children we had, and which one we got rid of... - int indexOne = getNthBit(previousChildMask, 1); - int indexTwo = getNthBit(previousChildMask, 2); - - bool removeChildOne = indexOne == childIndex; - bool removeChildTwo = indexTwo == childIndex; - - OctreeElement* childOne; - OctreeElement* childTwo; - OctreeElement* childThree; - - // Get the existing two children out of their encoding... - retrieveThreeChildren(childOne, childTwo, childThree); - - if (removeChildOne) { - childOne = childTwo; - childTwo = childThree; - } else if (removeChildTwo) { - childTwo = childThree; - } else { - // removing child three, nothing to do. - } - - storeTwoChildren(childOne, childTwo); - } else if (previousChildCount == 3 && newChildCount == 3) { - // If we had 3 children, and now have 3, then we need to determine which item we're replacing... - - // We need to determine which children we had, and which one we got rid of... - int indexOne = getNthBit(previousChildMask, 1); - int indexTwo = getNthBit(previousChildMask, 2); - - bool replaceChildOne = indexOne == childIndex; - bool replaceChildTwo = indexTwo == childIndex; - - OctreeElement* childOne; - OctreeElement* childTwo; - OctreeElement* childThree; - - // Get the existing two children out of their encoding... - retrieveThreeChildren(childOne, childTwo, childThree); - - if (replaceChildOne) { - childOne = child; - } else if (replaceChildTwo) { - childTwo = child; - } else { - childThree = child; - } - - storeThreeChildren(childOne, childTwo, childThree); - } else if (previousChildCount == 3 && newChildCount == 4) { - // If we had 3 children, and now have 4, then we know we are going to an external case... - - // First, decode the children... - OctreeElement* childOne; - OctreeElement* childTwo; - OctreeElement* childThree; - OctreeElement* childFour; - - // Get the existing two children out of their encoding... - retrieveThreeChildren(childOne, childTwo, childThree); - - // determine order of the existing children - int indexOne = getNthBit(previousChildMask, 1); - int indexTwo = getNthBit(previousChildMask, 2); - int indexThree = getNthBit(previousChildMask, 3); - - if (childIndex < indexOne) { - childFour = childThree; - childThree = childTwo; - childTwo = childOne; - childOne = child; - } else if (childIndex < indexTwo) { - childFour = childThree; - childThree = childTwo; - childTwo = child; - } else if (childIndex < indexThree) { - childFour = childThree; - childThree = child; - } else { - childFour = child; - } - - // now, allocate the external... - _childrenExternal = true; - const int newChildCount = 4; - _children.external = new OctreeElement*[newChildCount]; - memset(_children.external, 0, sizeof(OctreeElement*) * newChildCount); - - _externalChildrenMemoryUsage += newChildCount * sizeof(OctreeElement*); - - _children.external[0] = childOne; - _children.external[1] = childTwo; - _children.external[2] = childThree; - _children.external[3] = childFour; - _externalChildrenCount++; - } else if (previousChildCount == 4 && newChildCount == 3) { - // If we had 4 children, and now have 3, then we know we are going from an external case to a potential internal case - //assert(_children.external && _childrenExternal && previousChildCount == 4); - - // We need to determine which children we had, and which one we got rid of... - int indexOne = getNthBit(previousChildMask, 1); - int indexTwo = getNthBit(previousChildMask, 2); - int indexThree = getNthBit(previousChildMask, 3); - - bool removeChildOne = indexOne == childIndex; - bool removeChildTwo = indexTwo == childIndex; - bool removeChildThree = indexThree == childIndex; - - OctreeElement* childOne = _children.external[0]; - OctreeElement* childTwo = _children.external[1]; - OctreeElement* childThree = _children.external[2]; - OctreeElement* childFour = _children.external[3]; - - if (removeChildOne) { - childOne = childTwo; - childTwo = childThree; - childThree = childFour; - } else if (removeChildTwo) { - childTwo = childThree; - childThree = childFour; - } else if (removeChildThree) { - childThree = childFour; - } else { - // removing child four, nothing to do. - } - - // clean up the external children... - _childrenExternal = false; - delete[] _children.external; - _children.external = NULL; - _externalChildrenCount--; - _externalChildrenMemoryUsage -= previousChildCount * sizeof(OctreeElement*); - storeThreeChildren(childOne, childTwo, childThree); - } else if (previousChildCount == newChildCount) { - //assert(_children.external && _childrenExternal && previousChildCount >= 4); - //assert(previousChildCount == newChildCount); - - // 4 or more children, one item being replaced, we know we're stored externally, we just need to find the one - // that needs to be replaced and replace it. - for (int ordinal = 1; ordinal <= 8; ordinal++) { - int index = getNthBit(previousChildMask, ordinal); - if (index == childIndex) { - // this is our child to be replaced - int nthChild = ordinal-1; - _children.external[nthChild] = child; - break; - } - } - } else if (previousChildCount < newChildCount) { - // Growing case... previous must be 4 or greater - //assert(_children.external && _childrenExternal && previousChildCount >= 4); - //assert(previousChildCount == newChildCount-1); - - // 4 or more children, one item being added, we know we're stored externally, we just figure out where to insert - // this child pointer into our external list - OctreeElement** newExternalList = new OctreeElement*[newChildCount]; - memset(newExternalList, 0, sizeof(OctreeElement*) * newChildCount); - - int copiedCount = 0; - for (int ordinal = 1; ordinal <= newChildCount; ordinal++) { - int index = getNthBit(previousChildMask, ordinal); - if (index != -1 && index < childIndex) { - newExternalList[ordinal - 1] = _children.external[ordinal - 1]; - copiedCount++; - } else { - - // insert our new child here... - newExternalList[ordinal - 1] = child; - - // if we didn't copy all of our previous children, then we need to - if (copiedCount < previousChildCount) { - // our child needs to be inserted before this index, and everything else pushed out... - for (int oldOrdinal = ordinal; oldOrdinal <= previousChildCount; oldOrdinal++) { - newExternalList[oldOrdinal] = _children.external[oldOrdinal - 1]; - } - } - break; - } - } - delete[] _children.external; - _children.external = newExternalList; - _externalChildrenMemoryUsage -= previousChildCount * sizeof(OctreeElement*); - _externalChildrenMemoryUsage += newChildCount * sizeof(OctreeElement*); - - } else if (previousChildCount > newChildCount) { - //assert(_children.external && _childrenExternal && previousChildCount >= 4); - //assert(previousChildCount == newChildCount+1); - - // 4 or more children, one item being removed, we know we're stored externally, we just figure out which - // item to remove from our external list - OctreeElement** newExternalList = new OctreeElement*[newChildCount]; - - for (int ordinal = 1; ordinal <= previousChildCount; ordinal++) { - int index = getNthBit(previousChildMask, ordinal); - //assert(index != -1); - if (index < childIndex) { - newExternalList[ordinal - 1] = _children.external[ordinal - 1]; - } else { - // our child needs to be removed from here, and everything else pulled in... - for (int moveOrdinal = ordinal; moveOrdinal <= newChildCount; moveOrdinal++) { - newExternalList[moveOrdinal - 1] = _children.external[moveOrdinal]; - } - break; - } - } - delete[] _children.external; - _children.external = newExternalList; - _externalChildrenMemoryUsage -= previousChildCount * sizeof(OctreeElement*); - _externalChildrenMemoryUsage += newChildCount * sizeof(OctreeElement*); - } else { - //assert(false); - qCDebug(octree, "THIS SHOULD NOT HAPPEN previousChildCount == %d && newChildCount == %d",previousChildCount, newChildCount); - } - - // check to see if we could store these 4 children locally - if (getChildCount() == 4 && _childrenExternal && _children.external) { - checkStoreFourChildren(_children.external[0], _children.external[1], _children.external[2], _children.external[3]); - } - - -#ifdef HAS_AUDIT_CHILDREN - _childrenArray[childIndex] = child; - auditChildren("setChildAtIndex()"); -#endif // def HAS_AUDIT_CHILDREN - -#endif } diff --git a/libraries/octree/src/OctreeElement.h b/libraries/octree/src/OctreeElement.h index e2229b2214..830655242f 100644 --- a/libraries/octree/src/OctreeElement.h +++ b/libraries/octree/src/OctreeElement.h @@ -12,7 +12,6 @@ #ifndef hifi_OctreeElement_h #define hifi_OctreeElement_h -//#define HAS_AUDIT_CHILDREN //#define SIMPLE_CHILD_ARRAY #define SIMPLE_EXTERNAL_CHILDREN @@ -204,25 +203,9 @@ public: static quint64 getSetChildAtIndexTime() { return _setChildAtIndexTime; } static quint64 getSetChildAtIndexCalls() { return _setChildAtIndexCalls; } -#ifdef BLENDED_UNION_CHILDREN - static quint64 getSingleChildrenCount() { return _singleChildrenCount; } - static quint64 getTwoChildrenOffsetCount() { return _twoChildrenOffsetCount; } - static quint64 getTwoChildrenExternalCount() { return _twoChildrenExternalCount; } - static quint64 getThreeChildrenOffsetCount() { return _threeChildrenOffsetCount; } - static quint64 getThreeChildrenExternalCount() { return _threeChildrenExternalCount; } - static quint64 getCouldStoreFourChildrenInternally() { return _couldStoreFourChildrenInternally; } - static quint64 getCouldNotStoreFourChildrenInternally() { return _couldNotStoreFourChildrenInternally; } -#endif - static quint64 getExternalChildrenCount() { return _externalChildrenCount; } static quint64 getChildrenCount(int childCount) { return _childrenCount[childCount]; } -#ifdef BLENDED_UNION_CHILDREN -#ifdef HAS_AUDIT_CHILDREN - void auditChildren(const char* label) const; -#endif // def HAS_AUDIT_CHILDREN -#endif // def BLENDED_UNION_CHILDREN - enum ChildIndex { CHILD_BOTTOM_RIGHT_NEAR = 0, CHILD_BOTTOM_RIGHT_FAR = 1, @@ -261,15 +244,6 @@ protected: void deleteAllChildren(); void setChildAtIndex(int childIndex, OctreeElement* child); -#ifdef BLENDED_UNION_CHILDREN - void storeTwoChildren(OctreeElement* childOne, OctreeElement* childTwo); - void retrieveTwoChildren(OctreeElement*& childOne, OctreeElement*& childTwo); - void storeThreeChildren(OctreeElement* childOne, OctreeElement* childTwo, OctreeElement* childThree); - void retrieveThreeChildren(OctreeElement*& childOne, OctreeElement*& childTwo, OctreeElement*& childThree); - void decodeThreeOffsets(int64_t& offsetOne, int64_t& offsetTwo, int64_t& offsetThree) const; - void encodeThreeOffsets(int64_t offsetOne, int64_t offsetTwo, int64_t offsetThree); - void checkStoreFourChildren(OctreeElement* childOne, OctreeElement* childTwo, OctreeElement* childThree, OctreeElement* childFour); -#endif void calculateAACube(); void notifyDeleteHooks(); void notifyUpdateHooks(); @@ -296,19 +270,6 @@ protected: } _children; #endif -#ifdef BLENDED_UNION_CHILDREN - union children_t { - OctreeElement* single; - int32_t offsetsTwoChildren[2]; - quint64 offsetsThreeChildrenEncoded; - OctreeElement** external; - } _children; -#ifdef HAS_AUDIT_CHILDREN - OctreeElement* _childrenArray[8]; /// Only used when HAS_AUDIT_CHILDREN is enabled to help debug children encoding -#endif // def HAS_AUDIT_CHILDREN - -#endif //def BLENDED_UNION_CHILDREN - uint16_t _sourceUUIDKey; /// Client only, stores node id of voxel server that sent his voxel, 2 bytes // Support for _sourceUUID, we use these static member variables to track the UUIDs that are @@ -345,15 +306,6 @@ protected: static quint64 _setChildAtIndexTime; static quint64 _setChildAtIndexCalls; -#ifdef BLENDED_UNION_CHILDREN - static quint64 _singleChildrenCount; - static quint64 _twoChildrenOffsetCount; - static quint64 _twoChildrenExternalCount; - static quint64 _threeChildrenOffsetCount; - static quint64 _threeChildrenExternalCount; - static quint64 _couldStoreFourChildrenInternally; - static quint64 _couldNotStoreFourChildrenInternally; -#endif static quint64 _externalChildrenCount; static quint64 _childrenCount[NUMBER_OF_CHILDREN + 1]; }; diff --git a/libraries/octree/src/OctreeRenderer.cpp b/libraries/octree/src/OctreeRenderer.cpp index 7852f1d4b4..c4534b2565 100644 --- a/libraries/octree/src/OctreeRenderer.cpp +++ b/libraries/octree/src/OctreeRenderer.cpp @@ -12,9 +12,11 @@ #include #include -#include +#include #include #include +#include + #include "OctreeLogging.h" #include "OctreeRenderer.h" @@ -101,6 +103,15 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Shar sequence, flightTime, packetLength, dataBytes); } + _packetsInLastWindow++; + + int elementsPerPacket = 0; + int entitiesPerPacket = 0; + + quint64 totalWaitingForLock = 0; + quint64 totalUncompress = 0; + quint64 totalReadBitsteam = 0; + int subsection = 1; while (dataBytes > 0) { if (packetIsCompressed) { @@ -120,7 +131,12 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Shar // ask the VoxelTree to read the bitstream into the tree ReadBitstreamToTreeParams args(packetIsColored ? WANT_COLOR : NO_COLOR, WANT_EXISTS_BITS, NULL, sourceUUID, sourceNode, false, packetVersion); + quint64 startLock = usecTimestampNow(); + + // FIXME STUTTER - there may be an opportunity to bump this lock outside of the + // loop to reduce the amount of locking/unlocking we're doing _tree->lockForWrite(); + quint64 startUncompress = usecTimestampNow(); OctreePacketData packetData(packetIsCompressed); packetData.loadFinalizedContent(dataAt, sectionLength); if (extraDebugging) { @@ -134,17 +150,56 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Shar if (extraDebugging) { qCDebug(octree) << "OctreeRenderer::processDatagram() ******* START _tree->readBitstreamToTree()..."; } + quint64 startReadBitsteam = usecTimestampNow(); _tree->readBitstreamToTree(packetData.getUncompressedData(), packetData.getUncompressedSize(), args); + quint64 endReadBitsteam = usecTimestampNow(); if (extraDebugging) { qCDebug(octree) << "OctreeRenderer::processDatagram() ******* END _tree->readBitstreamToTree()..."; } _tree->unlock(); - + dataBytes -= sectionLength; dataAt += sectionLength; + + elementsPerPacket += args.elementsPerPacket; + entitiesPerPacket += args.entitiesPerPacket; + + _elementsInLastWindow += args.elementsPerPacket; + _entitiesInLastWindow += args.entitiesPerPacket; + + totalWaitingForLock += (startUncompress - startLock); + totalUncompress += (startReadBitsteam - startUncompress); + totalReadBitsteam += (endReadBitsteam - startReadBitsteam); + } + subsection++; + } + _elementsPerPacket.updateAverage(elementsPerPacket); + _entitiesPerPacket.updateAverage(entitiesPerPacket); + + _waitLockPerPacket.updateAverage(totalWaitingForLock); + _uncompressPerPacket.updateAverage(totalUncompress); + _readBitstreamPerPacket.updateAverage(totalReadBitsteam); + + quint64 now = usecTimestampNow(); + if (_lastWindowAt == 0) { + _lastWindowAt = now; + } + quint64 sinceLastWindow = now - _lastWindowAt; + + if (sinceLastWindow > USECS_PER_SECOND) { + float packetsPerSecondInWindow = (float)_packetsInLastWindow / (float)(sinceLastWindow / USECS_PER_SECOND); + float elementsPerSecondInWindow = (float)_elementsInLastWindow / (float)(sinceLastWindow / USECS_PER_SECOND); + float entitiesPerSecondInWindow = (float)_entitiesInLastWindow / (float)(sinceLastWindow / USECS_PER_SECOND); + _packetsPerSecond.updateAverage(packetsPerSecondInWindow); + _elementsPerSecond.updateAverage(elementsPerSecondInWindow); + _entitiesPerSecond.updateAverage(entitiesPerSecondInWindow); + + _lastWindowAt = now; + _packetsInLastWindow = 0; + _elementsInLastWindow = 0; + _entitiesInLastWindow = 0; } - subsection++; } } diff --git a/libraries/octree/src/OctreeRenderer.h b/libraries/octree/src/OctreeRenderer.h index 98026b732c..cf3b2a2ecd 100644 --- a/libraries/octree/src/OctreeRenderer.h +++ b/libraries/octree/src/OctreeRenderer.h @@ -61,12 +61,40 @@ public: /// clears the tree virtual void clear(); + float getAverageElementsPerPacket() const { return _elementsPerPacket.getAverage(); } + float getAverageEntitiesPerPacket() const { return _entitiesPerPacket.getAverage(); } + + float getAveragePacketsPerSecond() const { return _packetsPerSecond.getAverage(); } + float getAverageElementsPerSecond() const { return _elementsPerSecond.getAverage(); } + float getAverageEntitiesPerSecond() const { return _entitiesPerSecond.getAverage(); } + + float getAverageWaitLockPerPacket() const { return _waitLockPerPacket.getAverage(); } + float getAverageUncompressPerPacket() const { return _uncompressPerPacket.getAverage(); } + float getAverageReadBitstreamPerPacket() const { return _readBitstreamPerPacket.getAverage(); } + protected: virtual Octree* createTree() = 0; Octree* _tree; bool _managedTree; ViewFrustum* _viewFrustum; + + SimpleMovingAverage _elementsPerPacket; + SimpleMovingAverage _entitiesPerPacket; + + SimpleMovingAverage _packetsPerSecond; + SimpleMovingAverage _elementsPerSecond; + SimpleMovingAverage _entitiesPerSecond; + + SimpleMovingAverage _waitLockPerPacket; + SimpleMovingAverage _uncompressPerPacket; + SimpleMovingAverage _readBitstreamPerPacket; + + quint64 _lastWindowAt = 0; + int _packetsInLastWindow = 0; + int _elementsInLastWindow = 0; + int _entitiesInLastWindow = 0; + }; #endif // hifi_OctreeRenderer_h diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 93f3f345f0..1b94c70e57 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -910,6 +910,38 @@ bool Model::addToScene(std::shared_ptr scene, render::PendingChan return somethingAdded; } +bool Model::addToScene(std::shared_ptr scene, render::PendingChanges& pendingChanges, render::Item::Status::Getters& statusGetters) { + if (!_meshGroupsKnown && isLoadedWithTextures()) { + segregateMeshGroups(); + } + + bool somethingAdded = false; + + foreach (auto renderItem, _transparentRenderItems) { + auto item = scene->allocateID(); + auto renderData = MeshPartPayload::Pointer(renderItem); + auto renderPayload = render::PayloadPointer(new MeshPartPayload::Payload(renderData)); + renderPayload->addStatusGetters(statusGetters); + pendingChanges.resetItem(item, renderPayload); + _renderItems.insert(item, renderPayload); + somethingAdded = true; + } + + foreach (auto renderItem, _opaqueRenderItems) { + auto item = scene->allocateID(); + auto renderData = MeshPartPayload::Pointer(renderItem); + auto renderPayload = render::PayloadPointer(new MeshPartPayload::Payload(renderData)); + renderPayload->addStatusGetters(statusGetters); + pendingChanges.resetItem(item, renderPayload); + _renderItems.insert(item, renderPayload); + somethingAdded = true; + } + + _readyWhenAdded = readyToAddToScene(); + + return somethingAdded; +} + void Model::removeFromScene(std::shared_ptr scene, render::PendingChanges& pendingChanges) { foreach (auto item, _renderItems.keys()) { pendingChanges.removeItem(item); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 3748403b97..a6ce566a36 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -118,6 +118,7 @@ public: bool needsFixupInScene() { return !_readyWhenAdded && readyToAddToScene(); } bool readyToAddToScene(RenderArgs* renderArgs = nullptr) { return !_needsReload && isRenderable() && isActive() && isLoadedWithTextures(); } bool addToScene(std::shared_ptr scene, render::PendingChanges& pendingChanges); + bool addToScene(std::shared_ptr scene, render::PendingChanges& pendingChanges, render::Item::Status::Getters& statusGetters); void removeFromScene(std::shared_ptr scene, render::PendingChanges& pendingChanges); /// Sets the URL of the model to render. diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index e0c66eb604..12a6d32ae5 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -17,6 +17,8 @@ #include "RenderArgs.h" #include "TextureCache.h" +#include "render/DrawStatus.h" + #include #include "overlay3D_vert.h" @@ -49,6 +51,7 @@ RenderDeferredTask::RenderDeferredTask() : Task() { ))); _jobs.push_back(Job(new CullItems::JobModel("CullOpaque", _jobs.back().getOutput()))); _jobs.push_back(Job(new DepthSortItems::JobModel("DepthSortOpaque", _jobs.back().getOutput()))); + auto& renderedOpaques = _jobs.back().getOutput(); _jobs.push_back(Job(new DrawOpaqueDeferred::JobModel("DrawOpaqueDeferred", _jobs.back().getOutput()))); _jobs.push_back(Job(new DrawLight::JobModel("DrawLight"))); _jobs.push_back(Job(new ResetGLState::JobModel())); @@ -65,6 +68,11 @@ RenderDeferredTask::RenderDeferredTask() : Task() { _jobs.push_back(Job(new CullItems::JobModel("CullTransparent", _jobs.back().getOutput()))); _jobs.push_back(Job(new DepthSortItems::JobModel("DepthSortTransparent", _jobs.back().getOutput(), DepthSortItems(false)))); _jobs.push_back(Job(new DrawTransparentDeferred::JobModel("TransparentDeferred", _jobs.back().getOutput()))); + + _jobs.push_back(Job(new render::DrawStatus::JobModel("DrawStatus", renderedOpaques))); + _jobs.back().setEnabled(false); + _drawStatusJobIndex = _jobs.size() - 1; + _jobs.push_back(Job(new DrawOverlay3D::JobModel("DrawOverlay3D"))); _jobs.push_back(Job(new ResetGLState::JobModel())); } @@ -85,6 +93,9 @@ void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const Rend return; } + // Make sure we turn the displayItemStatus on/off + setDrawItemStatus(renderContext->_drawItemStatus); + renderContext->args->_context->syncCache(); for (auto job : _jobs) { @@ -135,7 +146,6 @@ void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const Rend void DrawTransparentDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) { assert(renderContext->args); assert(renderContext->args->_viewFrustum); - auto& renderDetails = renderContext->args->_details; RenderArgs* args = renderContext->args; gpu::Batch batch; diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 2f65c5ade6..3d11e97634 100755 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -70,6 +70,11 @@ public: render::Jobs _jobs; + int _drawStatusJobIndex = -1; + + void setDrawItemStatus(bool draw) { if (_drawStatusJobIndex >= 0) { _jobs[_drawStatusJobIndex].setEnabled(draw); } } + bool doDrawItemStatus() const { if (_drawStatusJobIndex >= 0) { return _jobs[_drawStatusJobIndex].isEnabled(); } else { return false; } } + virtual void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); }; diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp new file mode 100644 index 0000000000..90d167cc2a --- /dev/null +++ b/libraries/render/src/render/DrawStatus.cpp @@ -0,0 +1,174 @@ +// +// DrawStatus.cpp +// render/src/render +// +// Created by Niraj Venkat on 6/29/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 +// + +#include +#include + +#include "DrawStatus.h" + +#include +#include "gpu/GPULogging.h" + + +#include "gpu/Batch.h" +#include "gpu/Context.h" + +#include "ViewFrustum.h" +#include "RenderArgs.h" + +#include "drawItemBounds_vert.h" +#include "drawItemBounds_frag.h" +#include "drawItemStatus_vert.h" +#include "drawItemStatus_frag.h" + +using namespace render; + + + +const gpu::PipelinePointer& DrawStatus::getDrawItemBoundsPipeline() { + if (!_drawItemBoundsPipeline) { + auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(drawItemBounds_vert))); + auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(drawItemBounds_frag))); + gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); + + gpu::Shader::BindingSet slotBindings; + gpu::Shader::makeProgram(*program, slotBindings); + + _drawItemBoundPosLoc = program->getUniforms().findLocation("inBoundPos"); + _drawItemBoundDimLoc = program->getUniforms().findLocation("inBoundDim"); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + + state->setDepthTest(true, false, gpu::LESS_EQUAL); + + // Blend on transparent + state->setBlendFunction(true, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::DEST_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ZERO); + + // Good to go add the brand new pipeline + _drawItemBoundsPipeline.reset(gpu::Pipeline::create(program, state)); + } + return _drawItemBoundsPipeline; +} + +const gpu::PipelinePointer& DrawStatus::getDrawItemStatusPipeline() { + if (!_drawItemStatusPipeline) { + auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(drawItemStatus_vert))); + auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(drawItemStatus_frag))); + gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); + + gpu::Shader::BindingSet slotBindings; + gpu::Shader::makeProgram(*program, slotBindings); + + _drawItemStatusPosLoc = program->getUniforms().findLocation("inBoundPos"); + _drawItemStatusDimLoc = program->getUniforms().findLocation("inBoundDim"); + _drawItemStatusValueLoc = program->getUniforms().findLocation("inStatus"); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + + state->setDepthTest(false, false, gpu::LESS_EQUAL); + + // Blend on transparent + state->setBlendFunction(true, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::DEST_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ZERO); + + // Good to go add the brand new pipeline + _drawItemStatusPipeline.reset(gpu::Pipeline::create(program, state)); + } + return _drawItemStatusPipeline; +} + +void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) { + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + RenderArgs* args = renderContext->args; + auto& scene = sceneContext->_scene; + + // FIrst thing, we collect the bound and the status for all the items we want to render + int nbItems = 0; + { + if (!_itemBounds) { + _itemBounds.reset(new gpu::Buffer()); + } + if (!_itemStatus) { + _itemStatus.reset(new gpu::Buffer()); + } + + _itemBounds->resize((inItems.size() * sizeof(AABox))); + _itemStatus->resize((inItems.size() * sizeof(glm::vec4))); + AABox* itemAABox = reinterpret_cast (_itemBounds->editData()); + glm::ivec4* itemStatus = reinterpret_cast (_itemStatus->editData()); + for (auto& item : inItems) { + if (!item.bounds.isInvalid()) { + if (!item.bounds.isNull()) { + (*itemAABox) = item.bounds; + } else { + (*itemAABox).setBox(item.bounds.getCorner(), 0.1f); + } + auto& itemScene = scene->getItem(item.id); + (*itemStatus) = itemScene.getStatusPackedValues(); + + nbItems++; + itemAABox++; + itemStatus++; + } + } + } + + if (nbItems == 0) { + return; + } + + // Allright, something to render let's do it + gpu::Batch batch; + + glm::mat4 projMat; + Transform viewMat; + args->_viewFrustum->evalProjectionMatrix(projMat); + args->_viewFrustum->evalViewTransform(viewMat); + if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f)); + } + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + batch.setModelTransform(Transform()); + + // bind the one gpu::Pipeline we need + batch.setPipeline(getDrawItemBoundsPipeline()); + + AABox* itemAABox = reinterpret_cast (_itemBounds->editData()); + glm::ivec4* itemStatus = reinterpret_cast (_itemStatus->editData()); + + const unsigned int VEC3_ADRESS_OFFSET = 3; + + for (int i = 0; i < nbItems; i++) { + batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const GLfloat*) (itemAABox + i)); + batch._glUniform3fv(_drawItemBoundDimLoc, 1, ((const GLfloat*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); + + batch.draw(gpu::LINES, 24, 0); + } + + batch.setPipeline(getDrawItemStatusPipeline()); + for (int i = 0; i < nbItems; i++) { + batch._glUniform3fv(_drawItemStatusPosLoc, 1, (const GLfloat*) (itemAABox + i)); + batch._glUniform3fv(_drawItemStatusDimLoc, 1, ((const GLfloat*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); + batch._glUniform4iv(_drawItemStatusValueLoc, 1, (const GLint*) (itemStatus + i)); + + batch.draw(gpu::TRIANGLES, 24, 0); + } + + // Before rendering the batch make sure we re in sync with gl state + args->_context->syncCache(); + renderContext->args->_context->syncCache(); + args->_context->render((batch)); +} \ No newline at end of file diff --git a/libraries/render/src/render/DrawStatus.h b/libraries/render/src/render/DrawStatus.h new file mode 100644 index 0000000000..ca4763d33b --- /dev/null +++ b/libraries/render/src/render/DrawStatus.h @@ -0,0 +1,43 @@ +// +// DrawStatus.h +// render/src/render +// +// Created by Niraj Venkat on 6/29/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 +// + +#ifndef hifi_render_DrawStatus_h +#define hifi_render_DrawStatus_h + +#include "DrawTask.h" +#include "gpu/Batch.h" + +namespace render { + class DrawStatus { + int _drawItemBoundPosLoc = -1; + int _drawItemBoundDimLoc = -1; + int _drawItemStatusPosLoc = -1; + int _drawItemStatusDimLoc = -1; + int _drawItemStatusValueLoc = -1; + + gpu::Stream::FormatPointer _drawItemFormat; + gpu::PipelinePointer _drawItemBoundsPipeline; + gpu::PipelinePointer _drawItemStatusPipeline; + gpu::BufferPointer _itemBounds; + gpu::BufferPointer _itemStatus; + + public: + + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems); + + typedef Job::ModelI JobModel; + + const gpu::PipelinePointer& getDrawItemBoundsPipeline(); + const gpu::PipelinePointer& getDrawItemStatusPipeline(); + }; +} + +#endif // hifi_render_DrawStatus_h diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index 6076ec0006..6b028e6c4b 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -59,7 +59,6 @@ void render::cullItems(const SceneContextPointer& sceneContext, const RenderCont assert(renderContext->args); assert(renderContext->args->_viewFrustum); - auto& scene = sceneContext->_scene; RenderArgs* args = renderContext->args; auto renderDetails = renderContext->args->_details._item; @@ -101,7 +100,6 @@ void render::cullItems(const SceneContextPointer& sceneContext, const RenderCont void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemIDsBounds& outItems) { auto& scene = sceneContext->_scene; auto& items = scene->getMasterBucket().at(_filter); - auto& renderDetails = renderContext->args->_details; outItems.clear(); outItems.reserve(items.size()); @@ -128,9 +126,10 @@ struct ItemBound { float _nearDepth = 0.0f; float _farDepth = 0.0f; ItemID _id = 0; + AABox _bounds; ItemBound() {} - ItemBound(float centerDepth, float nearDepth, float farDepth, ItemID id) : _centerDepth(centerDepth), _nearDepth(nearDepth), _farDepth(farDepth), _id(id) {} + ItemBound(float centerDepth, float nearDepth, float farDepth, ItemID id, const AABox& bounds) : _centerDepth(centerDepth), _nearDepth(nearDepth), _farDepth(farDepth), _id(id), _bounds(bounds) {} }; struct FrontToBackSort { @@ -167,7 +166,7 @@ void render::depthSortItems(const SceneContextPointer& sceneContext, const Rende auto bound = itemDetails.bounds; // item.getBound(); float distance = args->_viewFrustum->distanceToCamera(bound.calcCenter()); - itemBounds.emplace_back(ItemBound(distance, distance, distance, itemDetails.id)); + itemBounds.emplace_back(ItemBound(distance, distance, distance, itemDetails.id, bound)); } // sort against Z @@ -181,7 +180,7 @@ void render::depthSortItems(const SceneContextPointer& sceneContext, const Rende // FInally once sorted result to a list of itemID for (auto& itemBound : itemBounds) { - outItems.emplace_back(itemBound._id); + outItems.emplace_back(ItemIDAndBounds(itemBound._id, itemBound._bounds)); } } diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h index 8a4d424005..a0139732f6 100755 --- a/libraries/render/src/render/DrawTask.h +++ b/libraries/render/src/render/DrawTask.h @@ -77,6 +77,9 @@ public: Job(const Job& other) : _concept(other._concept) {} ~Job(); + bool isEnabled() const { return _concept->isEnabled(); } + void setEnabled(bool isEnabled) { _concept->setEnabled(isEnabled); } + const std::string& getName() const { return _concept->getName(); } const Varying getInput() const { return _concept->getInput(); } const Varying getOutput() const { return _concept->getOutput(); } @@ -92,6 +95,7 @@ public: class Concept { std::string _name; + bool _isEnabled = true; public: Concept() : _name() {} Concept(const std::string& name) : _name(name) {} @@ -99,7 +103,10 @@ public: void setName(const std::string& name) { _name = name; } const std::string& getName() const { return _name; } - + + bool isEnabled() const { return _isEnabled; } + void setEnabled(bool isEnabled) { _isEnabled = isEnabled; } + virtual const Varying getInput() const { return Varying(); } virtual const Varying getOutput() const { return Varying(); } virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) = 0; @@ -119,7 +126,11 @@ public: Model(Data data): _data(data) {} Model(Data data, const std::string& name): Concept(name), _data(data) {} - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { jobRun(_data, sceneContext, renderContext); } + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + if (isEnabled()) { + jobRun(_data, sceneContext, renderContext); + } + } }; template class ModelI : public Concept { @@ -135,7 +146,11 @@ public: ModelI(const std::string& name, const Varying& input): Concept(name), _input(input) {} ModelI(const std::string& name, Data data): Concept(name), _data(data) {} - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { jobRunI(_data, sceneContext, renderContext, _input.get()); } + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + if (isEnabled()) { + jobRunI(_data, sceneContext, renderContext, _input.get()); + } + } }; template class ModelO : public Concept { @@ -155,7 +170,9 @@ public: ModelO(const std::string& name, Data data): Concept(name), _data(data), _output(Output()) {} void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - jobRunO(_data, sceneContext, renderContext, _output.edit()); + if (isEnabled()) { + jobRunO(_data, sceneContext, renderContext, _output.edit()); + } } }; @@ -177,7 +194,11 @@ public: void setInput(const Varying& input) { _input = input; } - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { jobRunIO(_data, sceneContext, renderContext, _input.get(), _output.edit()); } + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + if (isEnabled()) { + jobRunIO(_data, sceneContext, renderContext, _input.get(), _output.edit()); + } + } }; std::shared_ptr _concept; diff --git a/libraries/render/src/render/Engine.h b/libraries/render/src/render/Engine.h index 054f7e5ce4..1c600b13d6 100644 --- a/libraries/render/src/render/Engine.h +++ b/libraries/render/src/render/Engine.h @@ -49,6 +49,8 @@ public: int _numDrawnOverlay3DItems = 0; int _maxDrawnOverlay3DItems = -1; + bool _drawItemStatus = false; + RenderContext() {} }; typedef std::shared_ptr RenderContextPointer; diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index 1d2e54541b..a7145af4b5 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -10,6 +10,8 @@ // #include "Scene.h" +#include + using namespace render; void ItemBucketMap::insert(const ItemID& id, const ItemKey& key) { @@ -53,6 +55,50 @@ void ItemBucketMap::allocateStandardOpaqueTranparentBuckets() { (*this)[ItemFilter::Builder::transparentShape().withLayered()]; } +const Item::Status::Value Item::Status::Value::INVALID = Item::Status::Value(); + +const float Item::Status::Value::RED = 0.0f; +const float Item::Status::Value::YELLOW = 60.0f; +const float Item::Status::Value::GREEN = 120.0f; +const float Item::Status::Value::CYAN = 180.0f; +const float Item::Status::Value::BLUE = 240.0f; +const float Item::Status::Value::MAGENTA = 300.0f; + +void Item::Status::Value::setScale(float scale) { + _scale = (std::numeric_limits::max() -1) * 0.5f * (1.0f + std::max(std::min(scale, 1.0f), 0.0f)); + } + +void Item::Status::Value::setColor(float hue) { + // Convert the HUe from range [0, 360] to signed normalized value + const float HUE_MAX = 360.0f; + _color = (std::numeric_limits::max() - 1) * 0.5f * (1.0f + std::max(std::min(hue, HUE_MAX), 0.0f) / HUE_MAX); +} + +void Item::Status::getPackedValues(glm::ivec4& values) const { + for (unsigned int i = 0; i < values.length(); i++) { + if (i < _values.size()) { + values[i] = _values[i]().getPackedData(); + } else { + values[i] = Value::INVALID.getPackedData(); + } + } +} + +void Item::PayloadInterface::addStatusGetter(const Status::Getter& getter) { + if (!_status) { + _status.reset(new Status()); + } + _status->addGetter(getter); +} + +void Item::PayloadInterface::addStatusGetters(const Status::Getters& getters) { + if (!_status) { + _status.reset(new Status()); + } + for (auto& g : getters) { + _status->addGetter(g); + } +} void Item::resetPayload(const PayloadPointer& payload) { if (!payload) { @@ -63,6 +109,15 @@ void Item::resetPayload(const PayloadPointer& payload) { } } +glm::ivec4 Item::getStatusPackedValues() const { + glm::ivec4 values(Status::Value::INVALID.getPackedData()); + auto& status = getStatus(); + if (status) { + status->getPackedValues(values); + }; + return values; +} + void PendingChanges::resetItem(ItemID id, const PayloadPointer& payload) { _resetItems.push_back(id); _resetPayloads.push_back(payload); diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index 5ec9f0c951..934b460e52 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -196,12 +196,50 @@ public: // 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. + // Status 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 { + class Status { public: - int _firstFrame; + + // Status::Value class is the data used to represent the transient information of a status as a square icon + // The "icon" is a square displayed in the 3D scene over the render::Item AABB center. + // It can be scaled in the range [0, 1] and the color hue in the range [0, 360] representing the color wheel hue + class Value { + unsigned short _scale = 0xFFFF; + unsigned short _color = 0xFFFF; + public: + const static Value INVALID; // Invalid value meanss the status won't show + + Value() {} + Value(float scale, float hue) { setScale(scale); setColor(hue); } + + // It can be scaled in the range [0, 1] + void setScale(float scale); + // the color hue in the range [0, 360] representing the color wheel hue + void setColor(float hue); + + // Standard color Hue + static const float RED; // 0.0f; + static const float YELLOW; // 60.0f; + static const float GREEN; // 120.0f; + static const float CYAN; // 180.0f; + static const float BLUE; // 240.0f; + static const float MAGENTA; // 300.0f; + + // Retreive the Value data tightely packed as an int + int getPackedData() const { return *((const int*) this); } + }; + + typedef std::function Getter; + typedef std::vector Getters; + + Getters _values; + + void addGetter(const Getter& getter) { _values.push_back(getter); } + + void getPackedValues(glm::ivec4& values) const; }; + typedef std::shared_ptr StatusPointer; // Update Functor class UpdateFunctorInterface { @@ -222,7 +260,15 @@ public: virtual const model::MaterialKey getMaterialKey() const = 0; ~PayloadInterface() {} + + // Status interface is local to the base class + const StatusPointer& getStatus() const { return _status; } + void addStatusGetter(const Status::Getter& getter); + void addStatusGetters(const Status::Getters& getters); + protected: + StatusPointer _status; + friend class Item; virtual void update(const UpdateFunctorPointer& functor) = 0; }; @@ -253,6 +299,10 @@ public: // Shape Type Interface const model::MaterialKey getMaterialKey() const { return _payload->getMaterialKey(); } + // Access the status + const StatusPointer& getStatus() const { return _payload->getStatus(); } + glm::ivec4 getStatusPackedValues() const; + protected: PayloadPointer _payload; ItemKey _key; diff --git a/libraries/render/src/render/drawItemBounds.slf b/libraries/render/src/render/drawItemBounds.slf new file mode 100644 index 0000000000..b5d1a297bf --- /dev/null +++ b/libraries/render/src/render/drawItemBounds.slf @@ -0,0 +1,19 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// drawItemBounds.frag +// fragment shader +// +// Created by Sam Gateau on 6/29/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 +// + +varying vec4 varColor; + + +void main(void) { + gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); +} diff --git a/libraries/render/src/render/drawItemBounds.slv b/libraries/render/src/render/drawItemBounds.slv new file mode 100644 index 0000000000..4cb9a82371 --- /dev/null +++ b/libraries/render/src/render/drawItemBounds.slv @@ -0,0 +1,57 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// drawItemBounds.slv +// vertex shader +// +// Created by Sam Gateau on 6/29/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 +// + +<@include gpu/Transform.slh@> + +<$declareStandardTransform()$> + +uniform vec3 inBoundPos; +uniform vec3 inBoundDim; + +void main(void) { + const vec4 UNIT_BOX[8] = vec4[8]( + vec4(0.0, 0.0, 0.0, 1.0), + vec4(1.0, 0.0, 0.0, 1.0), + vec4(0.0, 1.0, 0.0, 1.0), + vec4(1.0, 1.0, 0.0, 1.0), + vec4(0.0, 0.0, 1.0, 1.0), + vec4(1.0, 0.0, 1.0, 1.0), + vec4(0.0, 1.0, 1.0, 1.0), + vec4(1.0, 1.0, 1.0, 1.0) + ); + const int UNIT_BOX_LINE_INDICES[24] = int[24]( + 0, 1, + 1, 3, + 3, 2, + 2, 0, + 4, 5, + 5, 7, + 7, 6, + 6, 4, + 2, 6, + 3, 7, + 0, 4, + 1, 5 + ); + vec4 pos = UNIT_BOX[UNIT_BOX_LINE_INDICES[gl_VertexID]]; + + pos.xyz = inBoundPos + inBoundDim * pos.xyz; + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToClipPos(cam, obj, pos, gl_Position)$> + + // varTexcoord = (pos.xy + 1) * 0.5; +} \ No newline at end of file diff --git a/libraries/render/src/render/drawItemStatus.slf b/libraries/render/src/render/drawItemStatus.slf new file mode 100644 index 0000000000..dcf5d3ee25 --- /dev/null +++ b/libraries/render/src/render/drawItemStatus.slf @@ -0,0 +1,19 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// drawItemStatus.frag +// fragment shader +// +// Created by Sam Gateau on 6/30/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 +// + +varying vec4 varColor; + + +void main(void) { + gl_FragColor = varColor; +} diff --git a/libraries/render/src/render/drawItemStatus.slv b/libraries/render/src/render/drawItemStatus.slv new file mode 100644 index 0000000000..9e2b4919ff --- /dev/null +++ b/libraries/render/src/render/drawItemStatus.slv @@ -0,0 +1,101 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// drawItemStatus.slv +// vertex shader +// +// Created by Sam Gateau on 6/30/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 +// + +<@include gpu/Transform.slh@> + +<$declareStandardTransform()$> + +varying vec4 varColor; + +uniform vec3 inBoundPos; +uniform vec3 inBoundDim; +uniform ivec4 inStatus; + +vec3 paintRainbow(float nv) { + float v = nv * 5.f; + if (v < 0.f) { + return vec3(0.f, 0.f, 0.f); + } else if (v < 1.f) { + return vec3(1.f, v, 0.f); + } else if (v < 2.f) { + return vec3(1.f - (v-1.f), 1.f, 0.f); + } else if (v < 3.f) { + return vec3(0.f, 1.f, (v-2.f)); + } else if (v < 4.f) { + return vec3(0.f, 1.f - (v-3.f), 1.f ); + } else if (v < 5.f) { + return vec3((v-4.f), 0.f, 1.f ); + } else { + return vec3(1.f, 1.f, 1.f); + } +} + +vec2 unpackStatus(int v) { + return vec2(clamp(float(int((v >> 0) & 0xFFFF) - 32727) / 32727.0, -1.0, 1.0), + clamp(float(int((v >> 16) & 0xFFFF) - 32727) / 32727.0, -1.0, 1.0)); +} + +void main(void) { + const vec2 ICON_PIXEL_SIZE = vec2(10, 10); + const vec2 MARGIN_PIXEL_SIZE = vec2(2, 2); + const int NUM_VERTICES = 6; + const vec4 UNIT_QUAD[NUM_VERTICES] = vec4[NUM_VERTICES]( + vec4(-1.0, -1.0, 0.0, 1.0), + vec4(1.0, -1.0, 0.0, 1.0), + vec4(-1.0, 1.0, 0.0, 1.0), + vec4(-1.0, 1.0, 0.0, 1.0), + vec4(1.0, -1.0, 0.0, 1.0), + vec4(1.0, 1.0, 0.0, 1.0) + ); + + // anchor point in clip space + vec4 anchorPoint = vec4(inBoundPos, 1.0) + vec4(inBoundDim, 0.0) * vec4(0.5, 0.5, 0.5, 0.0); + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToClipPos(cam, obj, anchorPoint, anchorPoint)$> + + // Which icon are we dealing with ? + int iconNum = gl_VertexID / NUM_VERTICES; + + // if invalid, just kill + if (inStatus[iconNum] == 0xFFFFFFFF) { + gl_Position = anchorPoint; + varColor = vec4(1.0); + return; + } + + // unpack to get x and y satus + vec2 iconStatus = unpackStatus(inStatus[iconNum]); + + // Use the status for showing a color + varColor = vec4(paintRainbow(abs(iconStatus.y)), 1.0); + + // Also changes the size of the notification + vec2 iconScale = ICON_PIXEL_SIZE; + iconScale = max(vec2(1, 1), (iconScale * iconStatus.x)); + + //Offset icon to the right based on the iconNum + vec2 offset = vec2(iconNum * (ICON_PIXEL_SIZE.x + MARGIN_PIXEL_SIZE.x), 0); + + // Final position in pixel space + int twoTriID = gl_VertexID - iconNum * NUM_VERTICES; + vec4 pos = UNIT_QUAD[twoTriID]; + vec2 quadPixelPos = offset.xy + pos.xy * 0.5 * iconScale; + + vec4 viewport; + <$transformCameraViewport(cam, viewport)$>; + vec2 pixelToClip = vec2(2.0 / viewport.z, 2.0 / viewport.w); + gl_Position = anchorPoint + (anchorPoint.w * vec4(quadPixelPos * pixelToClip, 0.0, 0.0)); + +} \ No newline at end of file diff --git a/libraries/script-engine/src/SceneScriptingInterface.h b/libraries/script-engine/src/SceneScriptingInterface.h index 8359aa58fa..674b452528 100644 --- a/libraries/script-engine/src/SceneScriptingInterface.h +++ b/libraries/script-engine/src/SceneScriptingInterface.h @@ -106,6 +106,10 @@ public: Q_INVOKABLE int getEngineMaxDrawnTransparentItems() { return _maxDrawnTransparentItems; } Q_INVOKABLE void setEngineMaxDrawnOverlay3DItems(int count) { _maxDrawnOverlay3DItems = count; } Q_INVOKABLE int getEngineMaxDrawnOverlay3DItems() { return _maxDrawnOverlay3DItems; } + + Q_INVOKABLE void setEngineDisplayItemStatus(bool display) { _drawItemStatus = display; } + Q_INVOKABLE bool doEngineDisplayItemStatus() { return _drawItemStatus; } + signals: void shouldRenderAvatarsChanged(bool shouldRenderAvatars); void shouldRenderEntitiesChanged(bool shouldRenderEntities); @@ -136,6 +140,8 @@ protected: int _maxDrawnTransparentItems = -1; int _maxDrawnOverlay3DItems = -1; + bool _drawItemStatus = false; + }; #endif // hifi_SceneScriptingInterface_h