Merge pull request #6365 from sethalves/rework-visual-physics-debug

grab fix + physics logging/debug changes
This commit is contained in:
Clément Brisset 2015-11-16 09:06:05 -08:00
commit b795b66b35
48 changed files with 692 additions and 274 deletions

View file

@ -17,6 +17,7 @@
#include "OctreeSendThread.h"
#include "OctreeServer.h"
#include "OctreeServerConsts.h"
#include "OctreeLogging.h"
quint64 startSceneSleepTime = 0;
quint64 endSceneSleepTime = 0;
@ -572,14 +573,12 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
OctreeServer::trackInsideTime((float)elapsedInsideUsecs);
}
if (somethingToSend && _myServer->wantsVerboseDebug()) {
qDebug() << "Hit PPS Limit, packetsSentThisInterval =" << packetsSentThisInterval
<< " maxPacketsPerInterval = " << maxPacketsPerInterval
<< " clientMaxPacketsPerInterval = " << clientMaxPacketsPerInterval;
qCDebug(octree) << "Hit PPS Limit, packetsSentThisInterval =" << packetsSentThisInterval
<< " maxPacketsPerInterval = " << maxPacketsPerInterval
<< " clientMaxPacketsPerInterval = " << clientMaxPacketsPerInterval;
}
// Here's where we can/should allow the server to send other data...
// send the environment packet
// TODO: should we turn this into a while loop to better handle sending multiple special packets

View file

@ -506,6 +506,7 @@ Grabber.prototype.activateEntity = function(entityID, grabbedProperties) {
if (data["refCount"] == 1) {
data["gravity"] = grabbedProperties.gravity;
data["ignoreForCollisions"] = grabbedProperties.ignoreForCollisions;
data["collisionsWillMove"] = grabbedProperties.collisionsWillMove;
var whileHeldProperties = {gravity: {x:0, y:0, z:0}};
if (invertSolidWhileHeld) {
whileHeldProperties["ignoreForCollisions"] = ! grabbedProperties.ignoreForCollisions;
@ -522,7 +523,8 @@ Grabber.prototype.deactivateEntity = function(entityID) {
if (data["refCount"] < 1) {
Entities.editEntity(entityID, {
gravity: data["gravity"],
ignoreForCollisions: data["ignoreForCollisions"]
ignoreForCollisions: data["ignoreForCollisions"],
collisionsWillMove: data["collisionsWillMove"]
});
data = null;
}

137
examples/grabInspector.js Normal file
View file

@ -0,0 +1,137 @@
//
// grabInspector.js
// examples
//
// Created by Seth Alves on 2015-9-30.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("libraries/utils.js");
var INSPECT_RADIUS = 10;
var overlays = {};
var toType = function(obj) {
return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
}
function grabDataToString(grabData) {
var result = "";
for (var argumentName in grabData) {
if (grabData.hasOwnProperty(argumentName)) {
if (argumentName == "type") {
continue;
}
var arg = grabData[argumentName];
var argType = toType(arg);
var argString = arg;
if (argType == "object") {
if (Object.keys(arg).length == 3) {
argString = vec3toStr(arg, 1);
}
} else if (argType == "number") {
argString = arg.toFixed(2);
}
result += argumentName + ": "
// + toType(arg) + " -- "
+ argString + "\n";
}
}
return result;
}
function updateOverlay(entityID, grabText) {
var properties = Entities.getEntityProperties(entityID, ["position", "dimensions"]);
var position = Vec3.sum(properties.position, {x:0, y:properties.dimensions.y, z:0});
if (entityID in overlays) {
var overlay = overlays[entityID];
Overlays.editOverlay(overlay, {
text: grabText,
position: position
});
} else {
var lines = grabText.split(/\r\n|\r|\n/);
var maxLineLength = lines[0].length;
for (var i = 1; i < lines.length; i++) {
if (lines[i].length > maxLineLength) {
maxLineLength = lines[i].length;
}
}
var textWidth = maxLineLength * 0.034; // XXX how to know this?
var textHeight = .5;
var numberOfLines = lines.length;
var textMargin = 0.05;
var lineHeight = (textHeight - (2 * textMargin)) / numberOfLines;
overlays[entityID] = Overlays.addOverlay("text3d", {
position: position,
dimensions: { x: textWidth, y: textHeight },
backgroundColor: { red: 0, green: 0, blue: 0},
color: { red: 255, green: 255, blue: 255},
topMargin: textMargin,
leftMargin: textMargin,
bottomMargin: textMargin,
rightMargin: textMargin,
text: grabText,
lineHeight: lineHeight,
alpha: 0.9,
backgroundAlpha: 0.9,
ignoreRayIntersection: true,
visible: true,
isFacingAvatar: true
});
}
}
function cleanup() {
for (var entityID in overlays) {
Overlays.deleteOverlay(overlays[entityID]);
}
}
Script.setInterval(function() {
var nearbyEntities = Entities.findEntities(MyAvatar.position, INSPECT_RADIUS);
for (var entityIndex = 0; entityIndex < nearbyEntities.length; entityIndex++) {
var entityID = nearbyEntities[entityIndex];
var userData = getEntityUserData(entityID);
var grabData = userData["grabKey"]
// {"grabbableKey":{"invertSolidWhileHeld":true},
// "grabKey":{"activated":true,"avatarId":"{6ea8b092-10e0-4058-888b-6facc40d0fe9}","refCount":1,"gravity":{"x":0,"y":0,"z":0},"ignoreForCollisions":0,"collisionsWillMove":1}
// }
if (typeof grabData != 'undefined') {
var grabText = grabDataToString(grabData);
updateOverlay(entityID, grabText);
} else {
if (entityID in overlays) {
Overlays.deleteOverlay(overlays[entityID]);
delete overlays[entityID];
}
}
}
// if an entity is too far away, remove its overlay
for (var entityID in overlays) {
var position = Entities.getEntityProperties(entityID, ["position"]).position;
if (Vec3.distance(position, MyAvatar.position) > INSPECT_RADIUS) {
Overlays.deleteOverlay(overlays[entityID]);
delete overlays[entityID];
}
}
}, 100);
Script.scriptEnding.connect(cleanup);

View file

@ -62,10 +62,24 @@ var overlaysCounter = new CounterWidget(panel, "Overlays",
);
panel.newCheckbox("Display status",
function(value) { Scene.setEngineDisplayItemStatus(value); },
function() { return Scene.doEngineDisplayItemStatus(); },
function(value) { return (value); }
// see libraries/render/src/render/Engine.h
var showDisplayStatusFlag = 1;
var showNetworkStatusFlag = 2;
panel.newCheckbox("Display status",
function(value) { Scene.setEngineDisplayItemStatus(value ?
Scene.doEngineDisplayItemStatus() | showDisplayStatusFlag :
Scene.doEngineDisplayItemStatus() & ~showDisplayStatusFlag); },
function() { return (Scene.doEngineDisplayItemStatus() & showDisplayStatusFlag) > 0; },
function(value) { return (value & showDisplayStatusFlag) > 0; }
);
panel.newCheckbox("Network/Physics status",
function(value) { Scene.setEngineDisplayItemStatus(value ?
Scene.doEngineDisplayItemStatus() | showNetworkStatusFlag :
Scene.doEngineDisplayItemStatus() & ~showNetworkStatusFlag); },
function() { return (Scene.doEngineDisplayItemStatus() & showNetworkStatusFlag) > 0; },
function(value) { return (value & showNetworkStatusFlag) > 0; }
);
var tickTackPeriod = 500;

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px" y="0px" width="1600px" height="100px" viewBox="0 0 1600 100" xml:space="preserve">
<path fill="#FFFFFF" d="M92.8,69.3c-0.8-1.5-1.9-2.7-3.1-3.8c4.4-11.4,3.7-24.6-2.6-35.8c-9.2-16.3-28.3-24.4-46.3-20l-3.1,0.7
l4.9,8.7l1.7-0.3c13.7-2.7,27.6,3.6,34.5,15.7c4.9,8.7,5.5,18.9,1.9,27.9c-2.1,0.1-4.3,0.6-6.2,1.8c-6.5,3.7-8.8,12-5.1,18.5
c3.7,6.5,12,8.8,18.5,5.1C94.2,84.1,96.5,75.8,92.8,69.3z"/>
<path fill="#FFFFFF" d="M54.2,82.6l-1.5,0.1c-12.3,0.8-24.2-5.6-30.2-16.3c-3.8-6.6-4.9-14.2-3.7-21.3c2.8,0.4,5.9-0.1,8.6-1.6
c6.5-3.7,8.8-12,5.1-18.5s-12-8.8-18.5-5.1C7.7,23.7,5.4,32,9,38.5c0.3,0.6,0.7,1.2,1.2,1.7c-2.6,10.3-1.4,21.5,4.1,31.1
c7.5,13.2,21.5,21.2,36.5,21.2c1.8,0,3.5-0.1,5.2-0.3l3.6-0.4L54.2,82.6z"/>
<path fill="#FFFFFF" d="M67.2,63.4H33.8c-1,0-2.1-0.5-2.6-1.5c-0.5-0.9-0.5-2.1,0-3L47.8,30c0.5-0.9,1.6-1.5,2.6-1.5
s2.1,0.5,2.6,1.5l16.7,28.9c0.5,0.9,0.5,2.1,0,3C69.3,62.9,68.3,63.4,67.2,63.4z M39,57.4h23L50.4,37.5L39,57.4z"/>
<polygon fill="#FFFFFF" points="175.4,30.6 149.9,8 123.9,30.7 139,30.7 139.2,59.6 161,59.3 160.8,30.7 "/>
<polygon fill="#FFFFFF" points="225.6,39.8 251.1,62.5 277.1,39.8 261.9,39.8 261.7,8.9 240,9.2 240.2,39.8 "/>
<path fill="#FFFFFF" d="M174.3,42.8c1.8,3.7,2.8,7.8,2.8,12.1c0,15.2-12.3,27.5-27.5,27.5c-15.2,0-27.5-12.3-27.5-27.5
c0-4.4,1-8.5,2.9-12.1h-7.9c-1.4,3.8-2.2,7.8-2.2,12.1c0,19.2,15.6,34.7,34.7,34.7c19.2,0,34.7-15.6,34.7-34.7
c0-4.3-0.8-8.3-2.2-12.1H174.3z"/>
<path fill="#FFFFFF" d="M278.8,53c0.1,0.7,0.1,1.5,0.1,2.2c0,15.2-12.4,27.6-27.6,27.6c-15.2,0-27.6-12.4-27.6-27.6
c0-1.1,0.1-2.1,0.2-3.1c-2.1-2.1-4.1-4.1-6.2-6.2c-0.8,3-1.3,6.1-1.3,9.3c0,19.2,15.6,34.9,34.9,34.9s34.9-15.6,34.9-34.9
c0-2.9-0.4-5.8-1.1-8.5L278.8,53z"/>
<circle fill="none" stroke="#000000" stroke-width="7" stroke-miterlimit="10" stroke-dasharray="7.7202,7.7202" cx="-174" cy="-5.8" r="14.7"/>
<path d="M-174-10.6c2.6,0,4.7,2.1,4.7,4.7s-2.1,4.7-4.7,4.7s-4.7-2.1-4.7-4.7S-176.6-10.6-174-10.6 M-174-17.6
c-6.5,0-11.7,5.3-11.7,11.7s5.3,11.7,11.7,11.7s11.7-5.3,11.7-11.7S-167.5-17.6-174-17.6L-174-17.6z"/>
<path fill="#FFFFFF" d="M353.3,91.2c-0.3,0-0.7,0-1,0c-1.8-0.2-3.5-0.4-5.3-0.7c-21.3-3.6-35.2-22.8-32-44.2
c2.7-18.2,17.7-31.4,36.8-32.5c17.2-0.9,33.8,11.4,38.2,28.5c0.8,3.1,1.1,6.3,1.6,9.5c0,0.3,0,0.7,0,1c-0.2,0.9-0.4,1.8-0.5,2.7
c-1.3,16.3-12.9,30.1-28.8,34C359.3,90.4,356.3,90.7,353.3,91.2z M353.7,83.9c8.3,0,16.1-3.4,22.6-9.9c2.2-2.2,2-3.1-0.7-4.5
c-3.9-1.9-7.8-3.7-11.7-5.6c-4-2-4.6-8.1-1.1-10.8c2-1.5,2.4-3.7,2.1-5.9c-0.2-1.8-1-3.5-1.2-5.3c-0.6-6-5.2-10.2-11.1-10.1
c-5.9,0.1-10.4,4.8-10.6,10.9c-0.1,1.4-0.4,2.8-0.9,4.1c-0.6,1.9,0.1,4.9,1.7,6.3c3.8,3.1,3.1,9-1.4,11.2c-3.6,1.7-7.2,3.4-10.8,5.2
c-3.4,1.6-3.6,2.5-0.8,5.1C336.2,80.6,343.8,83.9,353.7,83.9z"/>
<polygon fill="#FFFFFF" points="445.3,14.1 484.6,14.1 461.5,38.2 485.6,41.4 422.2,86.9 441.2,53.4 425.6,49.3 "/>
</svg>

After

Width:  |  Height:  |  Size: 3 KiB

View file

@ -3499,10 +3499,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls)) {
renderDebugFlags = (RenderArgs::DebugFlags) (renderDebugFlags | (int)RenderArgs::RENDER_DEBUG_HULLS);
}
if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowOwned)) {
renderDebugFlags =
(RenderArgs::DebugFlags) (renderDebugFlags | (int)RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP);
}
renderArgs->_debugFlags = renderDebugFlags;
//ViveControllerManager::getInstance().updateRendering(renderArgs, _main3DScene, pendingChanges);
}
@ -3562,6 +3558,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
renderContext._maxDrawnOverlay3DItems = sceneInterface->getEngineMaxDrawnOverlay3DItems();
renderContext._drawItemStatus = sceneInterface->doEngineDisplayItemStatus();
if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowOwned)) {
renderContext._drawItemStatus |= render::showNetworkStatusFlag;
}
renderContext._drawHitEffect = sceneInterface->doEngineDisplayHitEffect();
renderContext._occlusionStatus = Menu::getInstance()->isOptionChecked(MenuOption::DebugAmbientOcclusion);

View file

@ -212,6 +212,7 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
auto ownerEntity = _ownerEntity.lock();
if (ownerEntity) {
ownerEntity->setActionDataDirty(true);
ownerEntity->setActionDataNeedsTransmit(true);
}
});
activateBody();

View file

@ -20,7 +20,6 @@
#include <ObjectMotionState.h>
#include <PerfStat.h>
#include "RenderableDebugableEntityItem.h"
#include "../render-utils/simple_vert.h"
#include "../render-utils/simple_frag.h"
@ -63,6 +62,4 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
} else {
DependencyManager::get<DeferredLightingEffect>()->renderSolidCubeInstance(batch, getTransformToCenter(), cubeColor);
}
RenderableDebugableEntityItem::render(this, args);
};

View file

@ -1,67 +0,0 @@
//
// RenderableDebugableEntityItem.cpp
// libraries/entities-renderer/src/
//
// Created by Seth Alves on 5/1/15.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "RenderableDebugableEntityItem.h"
#include <glm/gtx/quaternion.hpp>
#include <gpu/Batch.h>
#include <DeferredLightingEffect.h>
#include <ObjectMotionState.h>
void RenderableDebugableEntityItem::renderBoundingBox(EntityItem* entity, RenderArgs* args,
float puffedOut, glm::vec4& color) {
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
auto shapeTransform = entity->getTransformToCenter();
if (puffedOut != 0.0f) {
shapeTransform.postScale(1.0f + puffedOut);
}
batch.setModelTransform(Transform()); // we want to include the scale as well
DependencyManager::get<DeferredLightingEffect>()->renderWireCubeInstance(batch, shapeTransform, color);
}
void RenderableDebugableEntityItem::render(EntityItem* entity, RenderArgs* args) {
if (args->_debugFlags & RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP) {
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
batch.setModelTransform(entity->getTransformToCenter()); // we want to include the scale as well
auto nodeList = DependencyManager::get<NodeList>();
const QUuid& myNodeID = nodeList->getSessionUUID();
bool highlightSimulationOwnership = (entity->getSimulatorID() == myNodeID);
if (highlightSimulationOwnership) {
glm::vec4 greenColor(0.0f, 1.0f, 0.2f, 1.0f);
renderBoundingBox(entity, args, 0.08f, greenColor);
}
quint64 now = usecTimestampNow();
if (now - entity->getLastEditedFromRemote() < 0.1f * USECS_PER_SECOND) {
glm::vec4 redColor(1.0f, 0.0f, 0.0f, 1.0f);
renderBoundingBox(entity, args, 0.16f, redColor);
}
if (now - entity->getLastBroadcast() < 0.2f * USECS_PER_SECOND) {
glm::vec4 yellowColor(1.0f, 1.0f, 0.2f, 1.0f);
renderBoundingBox(entity, args, 0.24f, yellowColor);
}
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(entity->getPhysicsInfo());
if (motionState && motionState->isActive()) {
glm::vec4 blueColor(0.0f, 0.0f, 1.0f, 1.0f);
renderBoundingBox(entity, args, 0.32f, blueColor);
}
}
}

View file

@ -1,23 +0,0 @@
//
// RenderableDebugableEntityItem.h
// libraries/entities-renderer/src/
//
// Created by Seth Alves on 5/1/15.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_RenderableDebugableEntityItem_h
#define hifi_RenderableDebugableEntityItem_h
#include <EntityItem.h>
class RenderableDebugableEntityItem {
public:
static void renderBoundingBox(EntityItem* entity, RenderArgs* args, float puffedOut, glm::vec4& color);
static void render(EntityItem* entity, RenderArgs* args);
};
#endif // hifi_RenderableDebugableEntityItem_h

View file

@ -10,6 +10,7 @@
//
#include <ObjectMotionState.h>
#include "RenderableEntityItem.h"
namespace render {
@ -40,5 +41,60 @@ namespace render {
}
}
void makeEntityItemStatusGetters(EntityItemPointer 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),
(unsigned char) RenderItemStatusIcon::PACKET_RECEIVED);
});
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),
(unsigned char)RenderItemStatusIcon::PACKET_SENT);
});
statusGetters.push_back([entity] () -> render::Item::Status::Value {
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(entity->getPhysicsInfo());
if (motionState && motionState->isActive()) {
return render::Item::Status::Value(1.0f, render::Item::Status::Value::BLUE,
(unsigned char)RenderItemStatusIcon::ACTIVE_IN_BULLET);
}
return render::Item::Status::Value(0.0f, render::Item::Status::Value::BLUE,
(unsigned char)RenderItemStatusIcon::ACTIVE_IN_BULLET);
});
statusGetters.push_back([entity] () -> render::Item::Status::Value {
auto nodeList = DependencyManager::get<NodeList>();
const QUuid& myNodeID = nodeList->getSessionUUID();
bool weOwnSimulation = entity->getSimulationOwner().matchesValidID(myNodeID);
if (weOwnSimulation) {
return render::Item::Status::Value(1.0f, render::Item::Status::Value::BLUE,
(unsigned char)RenderItemStatusIcon::SIMULATION_OWNER);
}
return render::Item::Status::Value(0.0f, render::Item::Status::Value::BLUE,
(unsigned char)RenderItemStatusIcon::SIMULATION_OWNER);
});
statusGetters.push_back([entity] () -> render::Item::Status::Value {
if (entity->hasActions()) {
return render::Item::Status::Value(1.0f, render::Item::Status::Value::GREEN,
(unsigned char)RenderItemStatusIcon::HAS_ACTIONS);
}
return render::Item::Status::Value(0.0f, render::Item::Status::Value::GREEN,
(unsigned char)RenderItemStatusIcon::HAS_ACTIONS);
});
}

View file

@ -15,13 +15,26 @@
#include <render/Scene.h>
#include <EntityItem.h>
// These or the icon "name" used by the render item status value, they correspond to the atlas texture used by the DrawItemStatus
// job in the current rendering pipeline defined as of now (11/2015) in render-utils/RenderDeferredTask.cpp.
enum class RenderItemStatusIcon {
ACTIVE_IN_BULLET = 0,
PACKET_SENT = 1,
PACKET_RECEIVED = 2,
SIMULATION_OWNER = 3,
HAS_ACTIONS = 4,
NONE = 255
};
void makeEntityItemStatusGetters(EntityItemPointer entity, render::Item::Status::Getters& statusGetters);
class RenderableEntityItemProxy {
public:
RenderableEntityItemProxy(EntityItemPointer entity) : entity(entity) { }
typedef render::Payload<RenderableEntityItemProxy> Payload;
typedef Payload::DataPointer Pointer;
EntityItemPointer entity;
};
@ -36,19 +49,23 @@ class SimpleRenderableEntityItem {
public:
bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
_myItem = scene->allocateID();
auto renderData = std::make_shared<RenderableEntityItemProxy>(self);
auto renderPayload = std::make_shared<RenderableEntityItemProxy::Payload>(renderData);
render::Item::Status::Getters statusGetters;
makeEntityItemStatusGetters(self, statusGetters);
renderPayload->addStatusGetters(statusGetters);
pendingChanges.resetItem(_myItem, renderPayload);
return true;
}
void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
pendingChanges.removeItem(_myItem);
}
private:
render::ItemID _myItem;
};
@ -62,5 +79,4 @@ private: \
SimpleRenderableEntityItem _renderHelper;
#endif // hifi_RenderableEntityItem_h

View file

@ -54,6 +54,4 @@ void RenderableLineEntityItem::render(RenderArgs* args) {
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch);
DependencyManager::get<GeometryCache>()->renderVertices(batch, gpu::LINE_STRIP, _lineVerticesID);
}
RenderableDebugableEntityItem::render(this, args);
};

View file

@ -13,7 +13,6 @@
#define hifi_RenderableLineEntityItem_h
#include <LineEntityItem.h>
#include "RenderableDebugableEntityItem.h"
#include "RenderableEntityItem.h"
#include <GeometryCache.h>

View file

@ -21,7 +21,9 @@
#include "EntityTreeRenderer.h"
#include "EntitiesRendererLogging.h"
#include "RenderableEntityItem.h"
#include "RenderableModelEntityItem.h"
#include "RenderableEntityItem.h"
EntityItemPointer RenderableModelEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
return std::make_shared<RenderableModelEntityItem>(entityID, properties);
@ -179,25 +181,6 @@ 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<render::Scene> scene,
render::PendingChanges& pendingChanges) {
_myMetaItem = scene->allocateID();
@ -209,11 +192,11 @@ bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_p
if (_model) {
render::Item::Status::Getters statusGetters;
makeEntityItemStatusGetters(this, statusGetters);
makeEntityItemStatusGetters(shared_from_this(), statusGetters);
// note: we don't care if the model fails to add items, we always added our meta item and therefore we return
// true so that the system knows our meta item is in the scene!
_model->addToScene(scene, pendingChanges, statusGetters);
_model->addToScene(scene, pendingChanges, statusGetters, _showCollisionHull);
}
return true;
@ -245,14 +228,16 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
// check to see if when we added our models to the scene they were ready, if they were not ready, then
// fix them up in the scene
if (_model->needsFixupInScene()) {
bool shouldShowCollisionHull = (args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS) > 0;
if (_model->needsFixupInScene() || _showCollisionHull != shouldShowCollisionHull) {
_showCollisionHull = shouldShowCollisionHull;
render::PendingChanges pendingChanges;
_model->removeFromScene(scene, pendingChanges);
render::Item::Status::Getters statusGetters;
makeEntityItemStatusGetters(this, statusGetters);
_model->addToScene(scene, pendingChanges, statusGetters);
makeEntityItemStatusGetters(shared_from_this(), statusGetters);
_model->addToScene(scene, pendingChanges, statusGetters, _showCollisionHull);
scene->enqueuePendingChanges(pendingChanges);
}
@ -274,7 +259,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
EntityTreeRenderer* renderer = static_cast<EntityTreeRenderer*>(args->_renderer);
getModel(renderer);
}
if (_model) {
// handle animations..
if (hasAnimation()) {
@ -320,11 +305,12 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
}
}
} else {
glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f);
RenderableDebugableEntityItem::renderBoundingBox(this, args, 0.0f, greenColor);
static glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f);
gpu::Batch& batch = *args->_batch;
auto shapeTransform = getTransformToCenter();
batch.setModelTransform(Transform()); // we want to include the scale as well
DependencyManager::get<DeferredLightingEffect>()->renderWireCubeInstance(batch, shapeTransform, greenColor);
}
RenderableDebugableEntityItem::render(this, args);
}
Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) {

View file

@ -16,7 +16,6 @@
#include <QStringList>
#include <ModelEntityItem.h>
#include "RenderableDebugableEntityItem.h"
class Model;
class EntityTreeRenderer;
@ -82,6 +81,8 @@ private:
bool _dimensionsInitialized = true;
render::ItemID _myMetaItem;
bool _showCollisionHull = false;
};
#endif // hifi_RenderableModelEntityItem_h

View file

@ -139,6 +139,9 @@ bool RenderableParticleEffectEntityItem::addToScene(EntityItemPointer self,
_renderItemId = scene->allocateID();
auto renderData = ParticlePayload::Pointer(particlePayload);
auto renderPayload = render::PayloadPointer(new ParticlePayload::Payload(renderData));
render::Item::Status::Getters statusGetters;
makeEntityItemStatusGetters(shared_from_this(), statusGetters);
renderPayload->addStatusGetters(statusGetters);
pendingChanges.resetItem(_renderItemId, renderPayload);
_scene = scene;
return true;

View file

@ -153,6 +153,4 @@ void RenderablePolyLineEntityItem::render(RenderArgs* args) {
batch.setInputBuffer(0, _verticesBuffer, 0, _format->getChannels().at(0)._stride);
batch.draw(gpu::TRIANGLE_STRIP, _numVertices, 0);
RenderableDebugableEntityItem::render(this, args);
};

View file

@ -14,7 +14,6 @@
#include <gpu/Batch.h>
#include <PolyLineEntityItem.h>
#include "RenderableDebugableEntityItem.h"
#include "RenderableEntityItem.h"
#include <GeometryCache.h>
#include <QReadWriteLock>

View file

@ -538,8 +538,6 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
batch._glUniform3f(voxelVolumeSizeLocation, _voxelVolumeSize.x, _voxelVolumeSize.y, _voxelVolumeSize.z);
batch.drawIndexed(gpu::TRIANGLES, mesh->getNumIndices(), 0);
RenderableDebugableEntityItem::render(this, args);
}
bool RenderablePolyVoxEntityItem::addToScene(EntityItemPointer self,
@ -551,6 +549,10 @@ bool RenderablePolyVoxEntityItem::addToScene(EntityItemPointer self,
auto renderData = PolyVoxPayload::Pointer(renderItem);
auto renderPayload = std::make_shared<PolyVoxPayload::Payload>(renderData);
render::Item::Status::Getters statusGetters;
makeEntityItemStatusGetters(shared_from_this(), statusGetters);
renderPayload->addStatusGetters(statusGetters);
pendingChanges.resetItem(_myItem, renderPayload);
return true;

View file

@ -21,7 +21,6 @@
#include <TextureCache.h>
#include "PolyVoxEntityItem.h"
#include "RenderableDebugableEntityItem.h"
#include "RenderableEntityItem.h"
#include "gpu/Context.h"

View file

@ -20,7 +20,6 @@
#include <GeometryCache.h>
#include <PerfStat.h>
#include "RenderableDebugableEntityItem.h"
#include "../render-utils/simple_vert.h"
#include "../render-utils/simple_frag.h"
@ -70,7 +69,4 @@ void RenderableSphereEntityItem::render(RenderArgs* args) {
batch.setModelTransform(Transform());
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphereInstance(batch, modelTransform, sphereColor);
}
RenderableDebugableEntityItem::render(this, args);
};

View file

@ -19,6 +19,8 @@
#include <GeometryCache.h>
#include <PerfStat.h>
#include "RenderableEntityItem.h"
// Sphere entities should fit inside a cube entity of the same size, so a sphere that has dimensions 1x1x1
// is a half unit sphere. However, the geometry cache renders a UNIT sphere, so we need to scale down.
static const float SPHERE_ENTITY_SCALE = 0.5f;
@ -112,7 +114,9 @@ void RenderableZoneEntityItem::render(RenderArgs* args) {
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
render::PendingChanges pendingChanges;
_model->removeFromScene(scene, pendingChanges);
_model->addToScene(scene, pendingChanges);
render::Item::Status::Getters statusGetters;
makeEntityItemStatusGetters(shared_from_this(), statusGetters);
_model->addToScene(scene, pendingChanges, false);
scene->enqueuePendingChanges(pendingChanges);
@ -203,7 +207,11 @@ bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, std::shared_pt
auto renderData = std::make_shared<RenderableZoneEntityItemMeta>(self);
auto renderPayload = std::make_shared<RenderableZoneEntityItemMeta::Payload>(renderData);
render::Item::Status::Getters statusGetters;
makeEntityItemStatusGetters(shared_from_this(), statusGetters);
renderPayload->addStatusGetters(statusGetters);
pendingChanges.resetItem(_myMetaItem, renderPayload);
return true;
}

View file

@ -304,3 +304,24 @@ QDataStream& operator>>(QDataStream& stream, EntityActionType& entityActionType)
entityActionType = (EntityActionType)actionTypeAsInt;
return stream;
}
QString serializedActionsToDebugString(QByteArray data) {
if (data.size() == 0) {
return QString();
}
QVector<QByteArray> serializedActions;
QDataStream serializedActionsStream(data);
serializedActionsStream >> serializedActions;
QString result;
foreach(QByteArray serializedAction, serializedActions) {
QDataStream serializedActionStream(serializedAction);
EntityActionType actionType;
QUuid actionID;
serializedActionStream >> actionType;
serializedActionStream >> actionID;
result += EntityActionInterface::actionTypeToString(actionType) + "-" + actionID.toString() + " ";
}
return result;
}

View file

@ -89,4 +89,6 @@ typedef std::shared_ptr<EntityActionInterface> EntityActionPointer;
QDataStream& operator<<(QDataStream& stream, const EntityActionType& entityActionType);
QDataStream& operator>>(QDataStream& stream, EntityActionType& entityActionType);
QString serializedActionsToDebugString(QByteArray data);
#endif // hifi_EntityActionInterface_h

View file

@ -630,6 +630,9 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
dataAt += bytes;
bytesRead += bytes;
if (wantTerseEditLogging() && _simulationOwner != newSimOwner) {
qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << newSimOwner;
}
if (_simulationOwner.set(newSimOwner)) {
_dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID;
}
@ -704,17 +707,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
READ_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL);
READ_ENTITY_PROPERTY(PROP_HREF, QString, setHref);
READ_ENTITY_PROPERTY(PROP_DESCRIPTION, QString, setDescription);
{ // When we own the simulation we don't accept updates to the entity's actions
// but since we're using macros below we have to temporarily modify overwriteLocalData.
// NOTE: this prevents userB from adding an action to an object1 when UserA
// has simulation ownership of it.
// TODO: figure out how to allow multiple users to update actions simultaneously
bool oldOverwrite = overwriteLocalData;
overwriteLocalData = overwriteLocalData && !weOwnSimulation;
READ_ENTITY_PROPERTY(PROP_ACTION_DATA, QByteArray, setActionData);
overwriteLocalData = oldOverwrite;
}
READ_ENTITY_PROPERTY(PROP_ACTION_DATA, QByteArray, setActionData);
bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
propertyFlags, overwriteLocalData, somethingChanged);
@ -737,7 +730,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
// this "new" data is actually slightly out of date. We calculate the time we need to skip forward and
// use our simulation helper routine to get a best estimate of where the entity should be.
float skipTimeForward = (float)(now - lastSimulatedFromBufferAdjusted) / (float)(USECS_PER_SECOND);
// we want to extrapolate the motion forward to compensate for packet travel time, but
// we don't want the side effect of flag setting.
simulateKinematicMotion(skipTimeForward, false);
@ -745,7 +738,6 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
if (overwriteLocalData) {
if (!_simulationOwner.matchesValidID(myNodeID)) {
_lastSimulated = now;
}
}
@ -996,6 +988,11 @@ EntityTreePointer EntityItem::getTree() const {
return tree;
}
bool EntityItem::wantTerseEditLogging() {
EntityTreePointer tree = getTree();
return tree ? tree->wantTerseEditLogging() : false;
}
glm::mat4 EntityItem::getEntityToWorldMatrix() const {
glm::mat4 translation = glm::translate(getPosition());
glm::mat4 rotation = glm::mat4_cast(getRotation());
@ -1492,20 +1489,35 @@ void EntityItem::updateCreated(uint64_t value) {
}
void EntityItem::setSimulationOwner(const QUuid& id, quint8 priority) {
if (wantTerseEditLogging() && (id != _simulationOwner.getID() || priority != _simulationOwner.getPriority())) {
qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << id << priority;
}
_simulationOwner.set(id, priority);
}
void EntityItem::setSimulationOwner(const SimulationOwner& owner) {
if (wantTerseEditLogging() && _simulationOwner != owner) {
qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << owner;
}
_simulationOwner.set(owner);
}
void EntityItem::updateSimulatorID(const QUuid& value) {
if (wantTerseEditLogging() && _simulationOwner.getID() != value) {
qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << value;
}
if (_simulationOwner.setID(value)) {
_dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID;
}
}
void EntityItem::clearSimulationOwnership() {
if (wantTerseEditLogging() && !_simulationOwner.isNull()) {
qCDebug(entities) << "sim ownership for" << getDebugName() << "is now null";
}
_simulationOwner.clear();
// don't bother setting the DIRTY_SIMULATOR_ID flag because clearSimulationOwnership()
// is only ever called entity-server-side and the flags are only used client-side
@ -1607,6 +1619,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* s
bool success = true;
serializeActions(success, _allActionsDataCache);
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
setActionDataNeedsTransmit(true);
return success;
}
return false;
@ -1649,7 +1662,7 @@ void EntityItem::deserializeActionsInternal() {
return;
}
EntityTreePointer entityTree = _element ? _element->getTree() : nullptr;
EntityTreePointer entityTree = getTree();
assert(entityTree);
EntitySimulation* simulation = entityTree ? entityTree->getSimulation() : nullptr;
assert(simulation);
@ -1770,6 +1783,7 @@ void EntityItem::serializeActions(bool& success, QByteArray& result) const {
serializedActionsStream << serializedActions;
if (result.size() >= _maxActionsDataSize) {
qDebug() << "EntityItem::serializeActions size is too large -- " << result.size() << ">=" << _maxActionsDataSize;
success = false;
return;
}

View file

@ -307,6 +307,7 @@ public:
QString getName() const { return _name; }
void setName(const QString& value) { _name = value; }
QString getDebugName() { return _name != "" ? _name : getID().toString(); }
bool getVisible() const { return _visible; }
void setVisible(bool value) { _visible = value; }
@ -381,6 +382,7 @@ public:
void setPhysicsInfo(void* data) { _physicsInfo = data; }
EntityTreeElementPointer getElement() const { return _element; }
EntityTreePointer getTree() const;
bool wantTerseEditLogging();
static void setSendPhysicsUpdates(bool value) { _sendPhysicsUpdates = value; }
static bool getSendPhysicsUpdates() { return _sendPhysicsUpdates; }
@ -406,7 +408,13 @@ public:
QList<QUuid> getActionIDs() { return _objectActions.keys(); }
QVariantMap getActionArguments(const QUuid& actionID) const;
void deserializeActions();
void setActionDataDirty(bool value) const { _actionDataDirty = value; }
bool actionDataDirty() const { return _actionDataDirty; }
void setActionDataNeedsTransmit(bool value) const { _actionDataNeedsTransmit = value; }
bool actionDataNeedsTransmit() const { return _actionDataNeedsTransmit; }
bool shouldSuppressLocationEdits() const;
void setSourceUUID(const QUuid& sourceUUID) { _sourceUUID = sourceUUID; }
@ -439,7 +447,7 @@ protected:
mutable bool _recalcAABox = true;
mutable bool _recalcMinAACube = true;
mutable bool _recalcMaxAACube = true;
float _glowLevel;
float _localRenderAlpha;
float _density = ENTITY_ITEM_DEFAULT_DENSITY; // kg/m^3
@ -510,6 +518,7 @@ protected:
void checkWaitingToRemove(EntitySimulation* simulation = nullptr);
mutable QSet<QUuid> _actionsToRemove;
mutable bool _actionDataDirty = false;
mutable bool _actionDataNeedsTransmit = false;
// _previouslyDeletedActions is used to avoid an action being re-added due to server round-trip lag
static quint64 _rememberDeletedActionTime;
mutable QHash<QUuid, quint64> _previouslyDeletedActions;

View file

@ -611,7 +611,8 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
const QVariantMap& arguments) {
QUuid actionID = QUuid::createUuid();
auto actionFactory = DependencyManager::get<EntityActionFactoryInterface>();
bool success = actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
bool success = false;
actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
// create this action even if the entity doesn't have physics info. it will often be the
// case that a script adds an action immediately after an object is created, and the physicsInfo
// is computed asynchronously.
@ -623,16 +624,16 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
return false;
}
EntityActionPointer action = actionFactory->factory(actionType, actionID, entity, arguments);
if (action) {
entity->addAction(simulation, action);
auto nodeList = DependencyManager::get<NodeList>();
const QUuid myNodeID = nodeList->getSessionUUID();
if (entity->getSimulatorID() != myNodeID) {
entity->flagForOwnership();
}
return true;
if (!action) {
return false;
}
return false;
success = entity->addAction(simulation, action);
auto nodeList = DependencyManager::get<NodeList>();
const QUuid myNodeID = nodeList->getSessionUUID();
if (entity->getSimulatorID() != myNodeID) {
entity->flagForOwnership();
}
return false; // Physics will cause a packet to be sent, so don't send from here.
});
if (success) {
return actionID;
@ -656,9 +657,12 @@ bool EntityScriptingInterface::updateAction(const QUuid& entityID, const QUuid&
}
bool EntityScriptingInterface::deleteAction(const QUuid& entityID, const QUuid& actionID) {
return actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
return entity->removeAction(simulation, actionID);
bool success = false;
actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
success = entity->removeAction(simulation, actionID);
return false; // Physics will cause a packet to be sent, so don't send from here.
});
return success;
}
QVector<QUuid> EntityScriptingInterface::getActionIDs(const QUuid& entityID) {

View file

@ -198,6 +198,10 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
properties.setVelocityChanged(false);
properties.setAngularVelocityChanged(false);
properties.setAccelerationChanged(false);
if (wantTerseEditLogging()) {
qCDebug(entities) << senderNode->getUUID() << "physical edits suppressed";
}
}
}
// else client accepts what the server says
@ -612,6 +616,14 @@ EntityItemPointer EntityTree::findEntityByEntityItemID(const EntityItemID& entit
}
void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList<QString>& changedProperties) {
static quint64 lastTerseLog = 0;
quint64 now = usecTimestampNow();
if (now - lastTerseLog > USECS_PER_SECOND) {
qCDebug(entities) << "-------------------------";
}
lastTerseLog = now;
if (properties.simulationOwnerChanged()) {
int simIndex = changedProperties.indexOf("simulationOwner");
if (simIndex >= 0) {
@ -619,6 +631,79 @@ void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList<Q
changedProperties[simIndex] = QString("simulationOwner:") + QString::number((int)simOwner.getPriority());
}
}
if (properties.velocityChanged()) {
int index = changedProperties.indexOf("velocity");
if (index >= 0) {
glm::vec3 value = properties.getVelocity();
QString changeHint = "0";
if (value.x + value.y + value.z > 0) {
changeHint = "+";
} else if (value.x + value.y + value.z < 0) {
changeHint = "-";
}
changedProperties[index] = QString("velocity:") + changeHint;
}
}
if (properties.gravityChanged()) {
int index = changedProperties.indexOf("gravity");
if (index >= 0) {
glm::vec3 value = properties.getGravity();
QString changeHint = "0";
if (value.x + value.y + value.z > 0) {
changeHint = "+";
} else if (value.x + value.y + value.z < 0) {
changeHint = "-";
}
changedProperties[index] = QString("gravity:") + changeHint;
}
}
if (properties.actionDataChanged()) {
int index = changedProperties.indexOf("actionData");
if (index >= 0) {
QByteArray value = properties.getActionData();
QString changeHint = serializedActionsToDebugString(value);
changedProperties[index] = QString("actionData:") + changeHint;
}
}
if (properties.ignoreForCollisionsChanged()) {
int index = changedProperties.indexOf("ignoreForCollisions");
if (index >= 0) {
bool value = properties.getIgnoreForCollisions();
QString changeHint = "0";
if (value) {
changeHint = "1";
}
changedProperties[index] = QString("ignoreForCollisions:") + changeHint;
}
}
if (properties.collisionsWillMoveChanged()) {
int index = changedProperties.indexOf("collisionsWillMove");
if (index >= 0) {
bool value = properties.getCollisionsWillMove();
QString changeHint = "0";
if (value) {
changeHint = "1";
}
changedProperties[index] = QString("collisionsWillMove:") + changeHint;
}
}
if (properties.lockedChanged()) {
int index = changedProperties.indexOf("locked");
if (index >= 0) {
bool value = properties.getLocked();
QString changeHint = "0";
if (value) {
changeHint = "1";
}
changedProperties[index] = QString("locked:") + changeHint;
}
}
}
int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* editData, int maxLength,
@ -673,7 +758,8 @@ int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* edi
if (wantTerseEditLogging()) {
QList<QString> changedProperties = properties.listChangedProperties();
fixupTerseEditLogging(properties, changedProperties);
qCDebug(entities) << "edit" << entityItemID.toString() << changedProperties;
qCDebug(entities) << senderNode->getUUID() << "edit" <<
existingEntity->getDebugName() << changedProperties;
}
endLogging = usecTimestampNow();
@ -703,7 +789,7 @@ int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* edi
if (wantTerseEditLogging()) {
QList<QString> changedProperties = properties.listChangedProperties();
fixupTerseEditLogging(properties, changedProperties);
qCDebug(entities) << "add" << entityItemID.toString() << changedProperties;
qCDebug(entities) << senderNode->getUUID() << "add" << entityItemID << changedProperties;
}
endLogging = usecTimestampNow();

View file

@ -157,7 +157,7 @@ void SimulationOwner::test() {
}
bool SimulationOwner::operator!=(const SimulationOwner& other) {
return (_id != other._id && _priority != other._priority);
return (_id != other._id || _priority != other._priority);
}
SimulationOwner& SimulationOwner::operator=(const SimulationOwner& other) {

View file

@ -331,7 +331,7 @@ public:
template <typename T> Iterator<T> begin() { return Iterator<T>(&edit<T>(0), _stride); }
template <typename T> Iterator<T> end() { return Iterator<T>(&edit<T>(getNum<T>()), _stride); }
template <typename T> Iterator<const T> cbegin() const { return Iterator<const T>(&get<T>(0), _stride); }
template <typename T> Iterator<const T> cbegin() const { return Iterator<const T>(&get<T>(), _stride); }
template <typename T> Iterator<const T> cend() const { return Iterator<const T>(&get<T>(getNum<T>()), _stride); }
// the number of elements of the specified type fitting in the view size

View file

@ -112,8 +112,8 @@ bool EntityMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine)
_outgoingPriority = NO_PRORITY;
} else {
_nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS;
if (engine->getSessionID() == _entity->getSimulatorID() || _entity->getSimulationPriority() > _outgoingPriority) {
// we own the simulation or our priority looses to remote
if (engine->getSessionID() == _entity->getSimulatorID() || _entity->getSimulationPriority() >= _outgoingPriority) {
// we own the simulation or our priority looses to (or ties with) remote
_outgoingPriority = NO_PRORITY;
}
}
@ -244,7 +244,7 @@ bool EntityMotionState::isCandidateForOwnership(const QUuid& sessionID) const {
return false;
}
assert(entityTreeIsLocked());
return _outgoingPriority != NO_PRORITY || sessionID == _entity->getSimulatorID();
return _outgoingPriority != NO_PRORITY || sessionID == _entity->getSimulatorID() || _entity->actionDataNeedsTransmit();
}
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
@ -292,7 +292,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
_serverPosition += dt * _serverVelocity;
}
if (_serverActionData != _entity->getActionData()) {
if (_entity->actionDataNeedsTransmit()) {
setOutgoingPriority(SCRIPT_EDIT_SIMULATION_PRIORITY);
return true;
}
@ -370,11 +370,15 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep, const QUuid& s
assert(_body);
assert(entityTreeIsLocked());
if (_entity->actionDataNeedsTransmit()) {
return true;
}
if (_entity->getSimulatorID() != sessionID) {
// we don't own the simulation, but maybe we should...
if (_outgoingPriority != NO_PRORITY) {
if (_outgoingPriority < _entity->getSimulationPriority()) {
// our priority looses to remote, so we don't bother to bid
// our priority loses to remote, so we don't bother to bid
_outgoingPriority = NO_PRORITY;
return false;
}
@ -456,7 +460,10 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q
properties.setVelocity(_serverVelocity);
properties.setAcceleration(_serverAcceleration);
properties.setAngularVelocity(_serverAngularVelocity);
properties.setActionData(_serverActionData);
if (_entity->actionDataNeedsTransmit()) {
_entity->setActionDataNeedsTransmit(false);
properties.setActionData(_serverActionData);
}
// set the LastEdited of the properties but NOT the entity itself
quint64 now = usecTimestampNow();
@ -526,7 +533,7 @@ void EntityMotionState::clearIncomingDirtyFlags() {
}
}
// virtual
// virtual
quint8 EntityMotionState::getSimulationPriority() const {
if (_entity) {
return _entity->getSimulationPriority();

View file

@ -130,6 +130,7 @@ bool ObjectActionOffset::updateArguments(QVariantMap arguments) {
auto ownerEntity = _ownerEntity.lock();
if (ownerEntity) {
ownerEntity->setActionDataDirty(true);
ownerEntity->setActionDataNeedsTransmit(true);
}
});
activateBody();

View file

@ -163,6 +163,7 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
auto ownerEntity = _ownerEntity.lock();
if (ownerEntity) {
ownerEntity->setActionDataDirty(true);
ownerEntity->setActionDataNeedsTransmit(true);
}
});
activateBody();

View file

@ -501,8 +501,10 @@ void Model::setVisibleInScene(bool newValue, std::shared_ptr<render::Scene> scen
}
bool Model::addToScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
if (!_meshGroupsKnown && isLoaded()) {
bool Model::addToScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges, bool showCollisionHull) {
if ((!_meshGroupsKnown || showCollisionHull != _showCollisionHull) && isLoaded()) {
_showCollisionHull = showCollisionHull;
segregateMeshGroups();
}
@ -525,8 +527,12 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene, render::PendingChan
return somethingAdded;
}
bool Model::addToScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges, render::Item::Status::Getters& statusGetters) {
if (!_meshGroupsKnown && isLoaded()) {
bool Model::addToScene(std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges,
render::Item::Status::Getters& statusGetters,
bool showCollisionHull) {
if ((!_meshGroupsKnown || showCollisionHull != _showCollisionHull) && isLoaded()) {
_showCollisionHull = showCollisionHull;
segregateMeshGroups();
}
@ -1139,8 +1145,14 @@ AABox Model::getPartBounds(int meshIndex, int partIndex) {
}
void Model::segregateMeshGroups() {
const FBXGeometry& geometry = _geometry->getFBXGeometry();
const std::vector<std::unique_ptr<NetworkMesh>>& networkMeshes = _geometry->getMeshes();
QSharedPointer<NetworkGeometry> networkGeometry;
if (_showCollisionHull && _collisionGeometry && _collisionGeometry->isLoaded()) {
networkGeometry = _collisionGeometry;
} else {
networkGeometry = _geometry;
}
const FBXGeometry& geometry = networkGeometry->getFBXGeometry();
const std::vector<std::unique_ptr<NetworkMesh>>& networkMeshes = networkGeometry->getMeshes();
_rig->makeAnimSkeleton(geometry);

View file

@ -76,10 +76,13 @@ public:
return !_needsReload && isRenderable() && isActive() && isLoaded();
}
bool initWhenReady(render::ScenePointer scene);
bool addToScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
bool addToScene(std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges,
render::Item::Status::Getters& statusGetters);
bool showCollisionHull = false);
bool addToScene(std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges,
render::Item::Status::Getters& statusGetters,
bool showCollisionHull = false);
void removeFromScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
void renderSetup(RenderArgs* args);
bool isRenderable() const { return !_meshStates.isEmpty() || (isActive() && _geometry->getMeshes().empty()); }
@ -368,6 +371,7 @@ private:
bool _readyWhenAdded = false;
bool _needsReload = true;
bool _needsUpdateClusterMatrices = true;
bool _showCollisionHull = false;
friend class MeshPartPayload;
protected:

View file

@ -12,6 +12,7 @@
#include "RenderDeferredTask.h"
#include <PerfStat.h>
#include <PathUtils.h>
#include <RenderArgs.h>
#include <ViewFrustum.h>
#include <gpu/Context.h>
@ -111,8 +112,11 @@ RenderDeferredTask::RenderDeferredTask() : Task() {
_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)));
// Grab a texture map representing the different status icons and assign that to the drawStatsuJob
auto iconMapPath = PathUtils::resourcesPath() + "icons/statusIconAtlas.svg";
auto statusIconMap = DependencyManager::get<TextureCache>()->getImageTexture(iconMapPath);
_jobs.push_back(Job(new render::DrawStatus::JobModel("DrawStatus", renderedOpaques, DrawStatus(statusIconMap))));
_jobs.back().setEnabled(false);
_drawStatusJobIndex = _jobs.size() - 1;
@ -387,4 +391,4 @@ void DrawBackgroundDeferred::run(const SceneContextPointer& sceneContext, const
});
args->_batch = nullptr;
}
}

View file

@ -97,7 +97,12 @@ public:
int _drawStatusJobIndex = -1;
int _drawHitEffectJobIndex = -1;
void setDrawItemStatus(bool draw) { if (_drawStatusJobIndex >= 0) { _jobs[_drawStatusJobIndex].setEnabled(draw); } }
void setDrawItemStatus(int draw) {
if (_drawStatusJobIndex >= 0) {
_jobs[_drawStatusJobIndex].setEnabled(draw > 0);
}
}
bool doDrawItemStatus() const { if (_drawStatusJobIndex >= 0) { return _jobs[_drawStatusJobIndex].isEnabled(); } else { return false; } }
void setDrawHitEffect(bool draw) { if (_drawHitEffectJobIndex >= 0) { _jobs[_drawHitEffectJobIndex].setEnabled(draw); } }

View file

@ -29,7 +29,7 @@ using namespace render;
const gpu::PipelinePointer& DrawStatus::getDrawItemBoundsPipeline() {
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)));
@ -56,18 +56,20 @@ const gpu::PipelinePointer& DrawStatus::getDrawItemBoundsPipeline() {
return _drawItemBoundsPipeline;
}
const gpu::PipelinePointer& DrawStatus::getDrawItemStatusPipeline() {
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;
slotBindings.insert(gpu::Shader::Binding(std::string("iconStatusMap"), 0));
gpu::Shader::makeProgram(*program, slotBindings);
_drawItemStatusPosLoc = program->getUniforms().findLocation("inBoundPos");
_drawItemStatusDimLoc = program->getUniforms().findLocation("inBoundDim");
_drawItemStatusValueLoc = program->getUniforms().findLocation("inStatus");
_drawItemStatusValue0Loc = program->getUniforms().findLocation("inStatus0");
_drawItemStatusValue1Loc = program->getUniforms().findLocation("inStatus1");
auto state = std::make_shared<gpu::State>();
@ -84,11 +86,23 @@ const gpu::PipelinePointer& DrawStatus::getDrawItemStatusPipeline() {
return _drawItemStatusPipeline;
}
void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) {
void DrawStatus::setStatusIconMap(const gpu::TexturePointer& map) {
_statusIconMap = map;
}
const gpu::TexturePointer DrawStatus::getStatusIconMap() const {
return _statusIconMap;
}
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;
const int NUM_STATUS_VEC4_PER_ITEM = 2;
const int VEC4_LENGTH = 4;
// FIrst thing, we collect the bound and the status for all the items we want to render
int nbItems = 0;
@ -101,7 +115,7 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex
}
_itemBounds->resize((inItems.size() * sizeof(AABox)));
_itemStatus->resize((inItems.size() * sizeof(glm::vec4)));
_itemStatus->resize((inItems.size() * NUM_STATUS_VEC4_PER_ITEM * sizeof(glm::vec4)));
AABox* itemAABox = reinterpret_cast<AABox*> (_itemBounds->editData());
glm::ivec4* itemStatus = reinterpret_cast<glm::ivec4*> (_itemStatus->editData());
for (auto& item : inItems) {
@ -112,11 +126,31 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex
(*itemAABox).setBox(item.bounds.getCorner(), 0.1f);
}
auto& itemScene = scene->getItem(item.id);
(*itemStatus) = itemScene.getStatusPackedValues();
auto itemStatusPointer = itemScene.getStatus();
if (itemStatusPointer) {
// Query the current status values, this is where the statusGetter lambda get called
auto&& currentStatusValues = itemStatusPointer->getCurrentValues();
int valueNum = 0;
for (int vec4Num = 0; vec4Num < NUM_STATUS_VEC4_PER_ITEM; vec4Num++) {
(*itemStatus) = glm::ivec4(Item::Status::Value::INVALID.getPackedData());
for (int component = 0; component < VEC4_LENGTH; component++) {
valueNum = vec4Num * VEC4_LENGTH + component;
if (valueNum < (int)currentStatusValues.size()) {
(*itemStatus)[component] = currentStatusValues[valueNum].getPackedData();
}
}
itemStatus++;
}
} else {
(*itemStatus) = glm::ivec4(Item::Status::Value::INVALID.getPackedData());
itemStatus++;
(*itemStatus) = glm::ivec4(Item::Status::Value::INVALID.getPackedData());
itemStatus++;
}
nbItems++;
itemAABox++;
itemStatus++;
}
}
}
@ -131,6 +165,7 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex
Transform viewMat;
args->_viewFrustum->evalProjectionMatrix(projMat);
args->_viewFrustum->evalViewTransform(viewMat);
batch.setViewportTransform(args->_viewport);
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat);
@ -144,20 +179,28 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex
const unsigned int VEC3_ADRESS_OFFSET = 3;
for (int i = 0; i < nbItems; i++) {
batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*) (itemAABox + i));
batch._glUniform3fv(_drawItemBoundDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET);
if ((renderContext->_drawItemStatus & showDisplayStatusFlag) > 0) {
for (int i = 0; i < nbItems; i++) {
batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*) (itemAABox + i));
batch._glUniform3fv(_drawItemBoundDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET);
batch.draw(gpu::LINES, 24, 0);
batch.draw(gpu::LINES, 24, 0);
}
}
batch.setResourceTexture(0, gpu::TextureView(getStatusIconMap(), 0));
batch.setPipeline(getDrawItemStatusPipeline());
for (int i = 0; i < nbItems; i++) {
batch._glUniform3fv(_drawItemStatusPosLoc, 1, (const float*) (itemAABox + i));
batch._glUniform3fv(_drawItemStatusDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET);
batch._glUniform4iv(_drawItemStatusValueLoc, 1, (const int*) (itemStatus + i));
batch.draw(gpu::TRIANGLES, 24, 0);
if ((renderContext->_drawItemStatus & showNetworkStatusFlag) > 0) {
for (int i = 0; i < nbItems; i++) {
batch._glUniform3fv(_drawItemStatusPosLoc, 1, (const float*) (itemAABox + i));
batch._glUniform3fv(_drawItemStatusDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET);
batch._glUniform4iv(_drawItemStatusValue0Loc, 1, (const int*)(itemStatus + NUM_STATUS_VEC4_PER_ITEM * i));
batch._glUniform4iv(_drawItemStatusValue1Loc, 1, (const int*)(itemStatus + NUM_STATUS_VEC4_PER_ITEM * i + 1));
batch.draw(gpu::TRIANGLES, 24 * NUM_STATUS_VEC4_PER_ITEM, 0);
}
}
batch.setResourceTexture(0, 0);
});
}

View file

@ -21,22 +21,30 @@ namespace render {
int _drawItemBoundDimLoc = -1;
int _drawItemStatusPosLoc = -1;
int _drawItemStatusDimLoc = -1;
int _drawItemStatusValueLoc = -1;
int _drawItemStatusValue0Loc = -1;
int _drawItemStatusValue1Loc = -1;
gpu::Stream::FormatPointer _drawItemFormat;
gpu::PipelinePointer _drawItemBoundsPipeline;
gpu::PipelinePointer _drawItemStatusPipeline;
gpu::BufferPointer _itemBounds;
gpu::BufferPointer _itemStatus;
gpu::TexturePointer _statusIconMap;
public:
DrawStatus() {}
DrawStatus(const gpu::TexturePointer statusIconMap) { setStatusIconMap(statusIconMap); }
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems);
typedef Job::ModelI<DrawStatus, ItemIDsBounds> JobModel;
const gpu::PipelinePointer& getDrawItemBoundsPipeline();
const gpu::PipelinePointer& getDrawItemStatusPipeline();
const gpu::PipelinePointer getDrawItemBoundsPipeline();
const gpu::PipelinePointer getDrawItemStatusPipeline();
void setStatusIconMap(const gpu::TexturePointer& map);
const gpu::TexturePointer getStatusIconMap() const;
};
}

View file

@ -143,7 +143,7 @@ public:
const Varying getInput() const { return _input; }
ModelI(const std::string& name, const Varying& input): Concept(name), _input(input) {}
ModelI(const std::string& name, const Varying& input, Data data = Data()) : Concept(name), _data(data), _input(input) {}
ModelI(const std::string& name, Data data): Concept(name), _data(data) {}
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {

View file

@ -25,6 +25,10 @@ public:
};
typedef std::shared_ptr<SceneContext> SceneContextPointer;
// see examples/utilities/tools/renderEngineDebug.js
const int showDisplayStatusFlag = 1;
const int showNetworkStatusFlag = 2;
class RenderContext {
public:
@ -49,7 +53,7 @@ public:
int _numDrawnOverlay3DItems = 0;
int _maxDrawnOverlay3DItems = -1;
bool _drawItemStatus = false;
int _drawItemStatus = 0;
bool _drawHitEffect = false;
bool _occlusionStatus = false;

View file

@ -72,17 +72,20 @@ void Item::Status::Value::setScale(float scale) {
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<unsigned short>::max() - 1) * 0.5f * (1.0f + std::max(std::min(hue, HUE_MAX), 0.0f) / HUE_MAX);
_color = (std::numeric_limits<unsigned char>::max()) * (std::max(std::min(hue, HUE_MAX), 0.0f) / HUE_MAX);
}
void Item::Status::Value::setIcon(unsigned char icon) {
_icon = icon;
}
void Item::Status::getPackedValues(glm::ivec4& values) const {
for (unsigned int i = 0; i < (unsigned int)values.length(); i++) {
if (i < _values.size()) {
values[i] = _values[i]().getPackedData();
} else {
values[i] = Value::INVALID.getPackedData();
}
Item::Status::Values Item::Status::getCurrentValues() const {
Values currentValues(_values.size());
auto currentValue = currentValues.begin();
for (auto& getter : _values) {
(*currentValue) = getter();
currentValue++;
}
return currentValues;
}
void Item::PayloadInterface::addStatusGetter(const Status::Getter& getter) {
@ -110,15 +113,6 @@ 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);

View file

@ -206,17 +206,21 @@ public:
// 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;
unsigned char _color = 0xFF;
unsigned char _icon = 0xFF;
public:
const static Value INVALID; // Invalid value meanss the status won't show
Value() {}
Value(float scale, float hue) { setScale(scale); setColor(hue); }
Value(float scale, float hue, unsigned char icon = 0xFF) { setScale(scale); setColor(hue); setIcon(icon); }
// 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);
// the icon to display in the range [0, 255], where 0 means no icon, just filled quad and anything else would
// hopefully have an icon available to display (see DrawStatusJob)
void setIcon(unsigned char icon);
// Standard color Hue
static const float RED; // 0.0f;
@ -237,7 +241,10 @@ public:
void addGetter(const Getter& getter) { _values.push_back(getter); }
void getPackedValues(glm::ivec4& values) const;
size_t getNumValues() const { return _values.size(); }
using Values = std::vector <Value>;
Values getCurrentValues() const;
};
typedef std::shared_ptr<Status> StatusPointer;
@ -301,7 +308,6 @@ public:
// Access the status
const StatusPointer& getStatus() const { return _payload->getStatus(); }
glm::ivec4 getStatusPackedValues() const;
protected:
PayloadPointer _payload;

View file

@ -12,9 +12,20 @@
//
in vec4 varColor;
in vec3 varTexcoord;
out vec4 outFragColor;
uniform sampler2D _icons;
vec2 getIconTexcoord(float icon, vec2 uv) {
const vec2 ICON_COORD_SIZE = vec2(0.0625, 1.0);
return vec2((uv.x + icon) * ICON_COORD_SIZE.x, uv.y * ICON_COORD_SIZE.y);
}
void main(void) {
outFragColor = varColor;
if (varTexcoord.z < 254.5) {
outFragColor = texture(_icons, getIconTexcoord(varTexcoord.z, varTexcoord.xy)) * varColor;
} else {
vec2 centerDir = varTexcoord.xy * 2.0f - 1.0f;
outFragColor = vec4(varColor.xyz, 1.0 - step(1.0f, dot(centerDir.xy, centerDir.xy)));
}
}

View file

@ -17,10 +17,12 @@
<$declareStandardTransform()$>
out vec4 varColor;
out vec3 varTexcoord;
uniform vec3 inBoundPos;
uniform vec3 inBoundDim;
uniform ivec4 inStatus;
uniform ivec4 inStatus0;
uniform ivec4 inStatus1;
vec3 paintRainbow(float normalizedHue) {
float v = normalizedHue * 6.f;
@ -43,16 +45,28 @@ vec3 paintRainbow(float normalizedHue) {
}
}
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));
const int INVALID_STATUS = int(0xFFFFFFFF);
const int MAX_NUM_ICONS = 8;
const int ICONS_PER_ROW = 4;
int getIconStatus(int icon) {
if (icon < ICONS_PER_ROW) {
return inStatus0[icon];
} else if (icon < MAX_NUM_ICONS) {
return inStatus1[icon - ICONS_PER_ROW];
}
return INVALID_STATUS;
}
vec3 unpackStatus(int v) {
return vec3(clamp(float(int((v >> 0) & 0xFFFF) - 32727) / 32727.0, -1.0, 1.0),
clamp(float(uint((v >> 16) & 0xFF)) / 255.0, 0.0, 1.0),
clamp(float(int((v >> 24) & 0xFF)), 0.0, 255.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](
const int NUM_VERTICES_PER_ICON = 6;
const vec4 UNIT_QUAD[NUM_VERTICES_PER_ICON] = vec4[NUM_VERTICES_PER_ICON](
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),
@ -61,6 +75,17 @@ void main(void) {
vec4(1.0, 1.0, 0.0, 1.0)
);
const vec2 ICON_PIXEL_SIZE = vec2(20, 20);
const vec2 MARGIN_PIXEL_SIZE = vec2(2, 2);
const vec2 ICON_GRID_SLOTS[MAX_NUM_ICONS] = vec2[MAX_NUM_ICONS](vec2(-1.5, 0.5),
vec2(-0.5, 0.5),
vec2(0.5, 0.5),
vec2(1.5, 0.5),
vec2(-1.5,-0.5),
vec2(-0.5,-0.5),
vec2(0.5, -0.5),
vec2(1.5, -0.5));
// 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();
@ -68,36 +93,43 @@ void main(void) {
<$transformModelToClipPos(cam, obj, anchorPoint, anchorPoint)$>
// Which icon are we dealing with ?
int iconNum = gl_VertexID / NUM_VERTICES;
int iconNum = gl_VertexID / NUM_VERTICES_PER_ICON;
int packedIconStatus = getIconStatus(iconNum);
// if invalid, just kill
if (inStatus[iconNum] == 0xFFFFFFFF) {
if (packedIconStatus == INVALID_STATUS) {
gl_Position = anchorPoint;
varColor = vec4(1.0);
return;
}
// Which quad vertex pos?
int twoTriID = gl_VertexID - iconNum * NUM_VERTICES_PER_ICON;
vec4 quadPos = UNIT_QUAD[twoTriID];
// unpack to get x and y satus
vec2 iconStatus = unpackStatus(inStatus[iconNum]);
vec3 iconStatus = unpackStatus(packedIconStatus);
// Use the status for showing a color
varColor = vec4(paintRainbow(abs(iconStatus.y)), 1.0);
// Pass the texcoord and the z texcoord is representing the texture icon
varTexcoord = vec3((quadPos.xy + 1.0) * 0.5, iconStatus.z);
// Also changes the size of the notification
vec2 iconScale = ICON_PIXEL_SIZE;
iconScale = max(vec2(1, 1), (iconScale * iconStatus.x));
iconScale = max(vec2(0, 0), (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);
vec2 gridOffset = ICON_GRID_SLOTS[iconNum];
vec2 offset = gridOffset * (ICON_PIXEL_SIZE + MARGIN_PIXEL_SIZE);
// 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;
vec2 quadPixelPos = offset.xy + quadPos.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));
}
}

View file

@ -107,8 +107,8 @@ public:
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; }
Q_INVOKABLE void setEngineDisplayItemStatus(int display) { _drawItemStatus = display; }
Q_INVOKABLE int doEngineDisplayItemStatus() { return _drawItemStatus; }
Q_INVOKABLE void setEngineDisplayHitEffect(bool display) { _drawHitEffect = display; }
Q_INVOKABLE bool doEngineDisplayHitEffect() { return _drawHitEffect; }
@ -143,7 +143,7 @@ protected:
int _maxDrawnTransparentItems = -1;
int _maxDrawnOverlay3DItems = -1;
bool _drawItemStatus = false;
int _drawItemStatus = 0;
bool _drawHitEffect = false;

View file

@ -68,17 +68,16 @@ public:
class RenderArgs {
public:
typedef std::function<bool(const RenderArgs* args, const AABox& bounds)> ShoudRenderFunctor;
enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE, MIRROR_RENDER_MODE };
enum RenderSide { MONO, STEREO_LEFT, STEREO_RIGHT };
enum DebugFlags {
RENDER_DEBUG_NONE = 0,
RENDER_DEBUG_HULLS = 1,
RENDER_DEBUG_SIMULATION_OWNERSHIP = 2,
RENDER_DEBUG_HULLS = 1
};
RenderArgs(std::shared_ptr<gpu::Context> context = nullptr,
OctreeRenderer* renderer = nullptr,
ViewFrustum* viewFrustum = nullptr,