diff --git a/examples/entityScripts/breakdanceEntity.js b/examples/entityScripts/breakdanceEntity.js new file mode 100644 index 0000000000..77980c0d13 --- /dev/null +++ b/examples/entityScripts/breakdanceEntity.js @@ -0,0 +1,88 @@ +// +// breakdanceEntity.js +// examples/entityScripts +// +// Created by Brad Hefta-Gaub on 9/3/15. +// Copyright 2015 High Fidelity, Inc. +// +// This is an example of an entity script which when assigned to an entity, will start the breakdance game if you grab and hold the entity +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + Script.include("../toys/breakdanceCore.js"); + Script.include("../libraries/utils.js"); + + var _this; + + // this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember + // our this object, so we can access it in cases where we're called without a this (like in the case of various global signals) + BreakdanceEntity = function() { + _this = this; + print("BreakdanceEntity constructor"); + }; + + BreakdanceEntity.prototype = { + + // update() will be called regulary, because we've hooked the update signal in our preload() function + // we will check out userData for the grabData. In the case of the hydraGrab script, it will tell us + // if we're currently being grabbed and if the person grabbing us is the current interfaces avatar. + // we will watch this for state changes and print out if we're being grabbed or released when it changes. + update: function() { + var GRAB_USER_DATA_KEY = "grabKey"; + + // because the update() signal doesn't have a valid this, we need to use our memorized _this to access our entityID + var entityID = _this.entityID; + + // we want to assume that if there is no grab data, then we are not being grabbed + var defaultGrabData = { activated: false, avatarId: null }; + + // this handy function getEntityCustomData() is available in utils.js and it will return just the specific section + // of user data we asked for. If it's not available it returns our default data. + var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, defaultGrabData); + + // if the grabData says we're being grabbed, and the owner ID is our session, then we are being grabbed by this interface + if (grabData.activated && grabData.avatarId == MyAvatar.sessionUUID) { + + if (!_this.beingGrabbed) { + // remember we're being grabbed so we can detect being released + _this.beingGrabbed = true; + breakdanceStart(); + print("I'm was grabbed..."); + } else { + breakdanceUpdate(); + } + + } else if (_this.beingGrabbed) { + + // if we are not being grabbed, and we previously were, then we were just released, remember that + // and print out a message + _this.beingGrabbed = false; + print("I'm was released..."); + breakdanceEnd(); + } + }, + + // preload() will be called when the entity has become visible (or known) to the interface + // it gives us a chance to set our local JavaScript object up. In this case it means: + // * remembering our entityID, so we can access it in cases where we're called without an entityID + // * connecting to the update signal so we can check our grabbed state + preload: function(entityID) { + this.entityID = entityID; + Script.update.connect(this.update); + }, + + // unload() will be called when our entity is no longer available. It may be because we were deleted, + // or because we've left the domain or quit the application. In all cases we want to unhook our connection + // to the update signal + unload: function(entityID) { + Script.update.disconnect(this.update); + }, + + }; + + // entity scripts always need to return a newly constructed object of our type + return new BreakdanceEntity(); +}) diff --git a/examples/entityScripts/detectGrabExample.js b/examples/entityScripts/detectGrabExample.js new file mode 100644 index 0000000000..cdc79e119d --- /dev/null +++ b/examples/entityScripts/detectGrabExample.js @@ -0,0 +1,81 @@ +// +// detectGrabExample.js +// examples/entityScripts +// +// Created by Brad Hefta-Gaub on 9/3/15. +// Copyright 2015 High Fidelity, Inc. +// +// This is an example of an entity script which when assigned to an entity, will detect when the entity is being grabbed by the hydraGrab script +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + Script.include("../libraries/utils.js"); + + var _this; + + // this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember + // our this object, so we can access it in cases where we're called without a this (like in the case of various global signals) + DetectGrabbed = function() { + _this = this; + }; + + DetectGrabbed.prototype = { + + // update() will be called regulary, because we've hooked the update signal in our preload() function + // we will check out userData for the grabData. In the case of the hydraGrab script, it will tell us + // if we're currently being grabbed and if the person grabbing us is the current interfaces avatar. + // we will watch this for state changes and print out if we're being grabbed or released when it changes. + update: function() { + var GRAB_USER_DATA_KEY = "grabKey"; + + // because the update() signal doesn't have a valid this, we need to use our memorized _this to access our entityID + var entityID = _this.entityID; + + // we want to assume that if there is no grab data, then we are not being grabbed + var defaultGrabData = { activated: false, avatarId: null }; + + // this handy function getEntityCustomData() is available in utils.js and it will return just the specific section + // of user data we asked for. If it's not available it returns our default data. + var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, defaultGrabData); + + // if the grabData says we're being grabbed, and the owner ID is our session, then we are being grabbed by this interface + if (grabData.activated && grabData.avatarId == MyAvatar.sessionUUID) { + + // remember we're being grabbed so we can detect being released + _this.beingGrabbed = true; + + // print out that we're being grabbed + print("I'm being grabbed..."); + + } else if (_this.beingGrabbed) { + + // if we are not being grabbed, and we previously were, then we were just released, remember that + // and print out a message + _this.beingGrabbed = false; + print("I'm was released..."); + } + }, + + // preload() will be called when the entity has become visible (or known) to the interface + // it gives us a chance to set our local JavaScript object up. In this case it means: + // * remembering our entityID, so we can access it in cases where we're called without an entityID + // * connecting to the update signal so we can check our grabbed state + preload: function(entityID) { + this.entityID = entityID; + Script.update.connect(this.update); + }, + + // unload() will be called when our entity is no longer available. It may be because we were deleted, + // or because we've left the domain or quit the application. In all cases we want to unhook our connection + // to the update signal + unload: function(entityID) { + Script.update.disconnect(this.update); + }, + }; + + // entity scripts always need to return a newly constructed object of our type + return new DetectGrabbed(); +}) diff --git a/examples/libraries/omniTool/models/wand.js b/examples/libraries/omniTool/models/wand.js index af28afb81c..e14c90b36a 100644 --- a/examples/libraries/omniTool/models/wand.js +++ b/examples/libraries/omniTool/models/wand.js @@ -91,10 +91,7 @@ Wand.prototype.setTipColors = function(color1, color2) { } Wand.prototype.onUpdate = function(deltaTime) { - logDebug("Z4"); - if (this.visible) { - logDebug("5"); var time = new Date().getTime() / 250; var scale1 = Math.abs(Math.sin(time)); var scale2 = Math.abs(Math.cos(time)); diff --git a/examples/toys/breakdanceCore.js b/examples/toys/breakdanceCore.js index 0f346537eb..9bf63850ab 100644 --- a/examples/toys/breakdanceCore.js +++ b/examples/toys/breakdanceCore.js @@ -11,8 +11,6 @@ // -Script.include("../libraries/utils.js"); - function getPositionPuppet() { var DISTANCE_IN_FRONT = 2; var DISTANCE_UP = 0.4; @@ -513,7 +511,7 @@ breakdanceUpdate = function(deltaTime) { } else { Overlays.editOverlay(textOverlay, { text: "pose:" + poses[poseValue].name + "\n" + "animation:" + poses[poseValue].animation }); var props = Entities.getEntityProperties(puppetEntityID); - print("puppetEntityID:" + puppetEntityID + "age:"+props.age); + //print("puppetEntityID:" + puppetEntityID + "age:"+props.age); Entities.editEntity(puppetEntityID, { animationURL: poses[poseValue].animation, lifetime: TEMPORARY_LIFETIME + props.age // renew lifetime diff --git a/examples/toys/breakdanceToy.js b/examples/toys/breakdanceToy.js index 841d5ad111..0190ab74e9 100644 --- a/examples/toys/breakdanceToy.js +++ b/examples/toys/breakdanceToy.js @@ -11,6 +11,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +Script.include("../libraries/utils.js"); Script.include("breakdanceCore.js"); breakdanceStart(); Script.update.connect(breakdanceUpdate); diff --git a/examples/toys/magBalls.js b/examples/toys/magBalls.js index 7c11a94e06..e163aa5ffd 100644 --- a/examples/toys/magBalls.js +++ b/examples/toys/magBalls.js @@ -34,7 +34,7 @@ MODE_INFO[BALL_EDIT_MODE_ADD] = { }, colors: [ COLORS.GREEN, COLORS.BLUE ], // FIXME use an http path or find a way to get the relative path to the file - url: "file:///" + Script.resolvePath('../html/magBalls/addMode.html').replace("c:", "C:"), + url: Script.resolvePath('../html/magBalls/addMode.html'), }; MODE_INFO[BALL_EDIT_MODE_DELETE] = { @@ -45,10 +45,9 @@ MODE_INFO[BALL_EDIT_MODE_DELETE] = { }, colors: [ COLORS.RED, COLORS.BLUE ], // FIXME use an http path or find a way to get the relative path to the file - url: "file:///" + Script.resolvePath('../html/magBalls/deleteMode.html').replace("c:", "C:"), + url: Script.resolvePath('../html/magBalls/deleteMode.html'), }; - var UI_POSITION_MODE_LABEL = Vec3.multiply(0.5, Vec3.sum(MODE_INFO[BALL_EDIT_MODE_ADD].uiPosition, MODE_INFO[BALL_EDIT_MODE_DELETE].uiPosition)); diff --git a/examples/voxels.js b/examples/voxels.js index d9049e2b0c..0776bce627 100644 --- a/examples/voxels.js +++ b/examples/voxels.js @@ -3,6 +3,7 @@ var shiftHeld = false; Script.include([ "libraries/toolBars.js", + "libraries/utils.js", ]); var isActive = false; @@ -12,24 +13,25 @@ var toolWidth = 50; var addingVoxels = false; var deletingVoxels = false; +var addingSpheres = false; +var deletingSpheres = false; -offAlpha = 0.5; -onAlpha = 0.9; +var offAlpha = 0.5; +var onAlpha = 0.9; +var editSphereRadius = 4; function floorVector(v) { return {x: Math.floor(v.x), y: Math.floor(v.y), z: Math.floor(v.z)}; } -function vectorToString(v){ - return "{" + v.x + ", " + v.x + ", " + v.x + "}"; -} - var toolBar = (function () { var that = {}, toolBar, activeButton, addVoxelButton, deleteVoxelButton, + addSphereButton, + deleteSphereButton, addTerrainButton; function initialize() { @@ -66,6 +68,24 @@ var toolBar = (function () { visible: false }); + addSphereButton = toolBar.addTool({ + imageURL: toolIconUrl + "sphere-add.svg", + subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, + height: toolHeight, + alpha: offAlpha, + visible: false + }); + + deleteSphereButton = toolBar.addTool({ + imageURL: toolIconUrl + "sphere-delete.svg", + subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, + height: toolHeight, + alpha: offAlpha, + visible: false + }); + addTerrainButton = toolBar.addTool({ imageURL: toolIconUrl + "voxel-terrain.svg", subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, @@ -78,6 +98,22 @@ var toolBar = (function () { that.setActive(false); } + function disableAllButtons() { + addingVoxels = false; + deletingVoxels = false; + addingSpheres = false; + deletingSpheres = false; + + toolBar.setAlpha(offAlpha, addVoxelButton); + toolBar.setAlpha(offAlpha, deleteVoxelButton); + toolBar.setAlpha(offAlpha, addSphereButton); + toolBar.setAlpha(offAlpha, deleteSphereButton); + + toolBar.selectTool(addVoxelButton, false); + toolBar.selectTool(deleteVoxelButton, false); + toolBar.selectTool(addSphereButton, false); + toolBar.selectTool(deleteSphereButton, false); + } that.setActive = function(active) { if (active != isActive) { @@ -91,6 +127,8 @@ var toolBar = (function () { that.showTools = function(doShow) { toolBar.showTool(addVoxelButton, doShow); toolBar.showTool(deleteVoxelButton, doShow); + toolBar.showTool(addSphereButton, doShow); + toolBar.showTool(deleteSphereButton, doShow); toolBar.showTool(addTerrainButton, doShow); }; @@ -103,37 +141,46 @@ var toolBar = (function () { } if (addVoxelButton === toolBar.clicked(clickedOverlay)) { - if (addingVoxels) { - addingVoxels = false; - deletingVoxels = false; - toolBar.setAlpha(offAlpha, addVoxelButton); - toolBar.setAlpha(offAlpha, deleteVoxelButton); - toolBar.selectTool(addVoxelButton, false); - toolBar.selectTool(deleteVoxelButton, false); - } else { + var wasAddingVoxels = addingVoxels; + disableAllButtons() + if (!wasAddingVoxels) { addingVoxels = true; - deletingVoxels = false; toolBar.setAlpha(onAlpha, addVoxelButton); - toolBar.setAlpha(offAlpha, deleteVoxelButton); } return true; } if (deleteVoxelButton === toolBar.clicked(clickedOverlay)) { - if (deletingVoxels) { - deletingVoxels = false; - addingVoxels = false; - toolBar.setAlpha(offAlpha, addVoxelButton); - toolBar.setAlpha(offAlpha, deleteVoxelButton); - } else { + var wasDeletingVoxels = deletingVoxels; + disableAllButtons() + if (!wasDeletingVoxels) { deletingVoxels = true; - addingVoxels = false; - toolBar.setAlpha(offAlpha, addVoxelButton); toolBar.setAlpha(onAlpha, deleteVoxelButton); } return true; } + if (addSphereButton === toolBar.clicked(clickedOverlay)) { + var wasAddingSpheres = addingSpheres + disableAllButtons() + if (!wasAddingSpheres) { + addingSpheres = true; + toolBar.setAlpha(onAlpha, addSphereButton); + } + return true; + } + + if (deleteSphereButton === toolBar.clicked(clickedOverlay)) { + var wasDeletingSpheres = deletingSpheres; + disableAllButtons() + if (!wasDeletingSpheres) { + deletingSpheres = true; + toolBar.setAlpha(onAlpha, deleteSphereButton); + } + return true; + } + + if (addTerrainButton === toolBar.clicked(clickedOverlay)) { addTerrainBlock(); return true; @@ -155,85 +202,212 @@ var toolBar = (function () { }()); + +function getTerrainAlignedLocation(pos) { + var posDiv16 = Vec3.multiply(pos, 1.0 / 16.0); + var posDiv16Floored = floorVector(posDiv16); + return Vec3.multiply(posDiv16Floored, 16.0); +} + + +function lookupTerrainForLocation(pos) { + var baseLocation = getTerrainAlignedLocation(pos); + entitiesAtLoc = Entities.findEntities(baseLocation, 1.0); + for (var i = 0; i < entitiesAtLoc.length; i++) { + var id = entitiesAtLoc[i]; + var properties = Entities.getEntityProperties(id); + if (properties.name == "terrain") { + return id; + } + } + + return false; +} + + +function grabLowestJointY() { + var jointNames = MyAvatar.getJointNames(); + var floorY = MyAvatar.position.y; + for (var jointName in jointNames) { + if (MyAvatar.getJointPosition(jointNames[jointName]).y < floorY) { + floorY = MyAvatar.getJointPosition(jointNames[jointName]).y; + } + } + return floorY; +} + + + function addTerrainBlock() { - - var myPosDiv16 = Vec3.multiply(Vec3.sum(MyAvatar.position, {x:8, x:8, z:8}), 1.0 / 16.0); - var myPosDiv16Floored = floorVector(myPosDiv16); - var baseLocation = Vec3.multiply(myPosDiv16Floored, 16.0); - - if (baseLocation.y + 8 > MyAvatar.position.y) { + var baseLocation = getTerrainAlignedLocation(Vec3.sum(MyAvatar.position, {x:8, y:8, z:8})); + if (baseLocation.y > MyAvatar.position.y) { baseLocation.y -= 16; } - print("myPosDiv16 is " + vectorToString(myPosDiv16)); - print("MyPosDiv16Floored is " + vectorToString(myPosDiv16Floored)); - print("baseLocation is " + vectorToString(baseLocation)); - - alreadyThere = Entities.findEntities(baseLocation, 1.0); - for (var i = 0; i < alreadyThere.length; i++) { - var id = alreadyThere[i]; - var properties = Entities.getEntityProperties(id); - if (properties.name == "terrain") { - print("already terrain there"); + var alreadyThere = lookupTerrainForLocation(baseLocation); + if (alreadyThere) { + // there is already a terrain block under MyAvatar. + // try in front of the avatar. + facingPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(8.0, Quat.getFront(Camera.getOrientation()))); + facingPosition = Vec3.sum(facingPosition, {x:8, y:8, z:8}); + baseLocation = getTerrainAlignedLocation(facingPosition); + alreadyThere = lookupTerrainForLocation(baseLocation); + if (alreadyThere) { return; } } - var polyVoxId = Entities.addEntity({ + var polyVoxID = Entities.addEntity({ type: "PolyVox", name: "terrain", position: baseLocation, - dimensions: { x: 16, y: 16, z: 16 }, - voxelVolumeSize: {x:16, y:16, z:16}, - voxelSurfaceStyle: 2 + dimensions: { x:16, y:16, z:16 }, + voxelVolumeSize: {x:16, y:64, z:16}, + voxelSurfaceStyle: 0, + xTextureURL: "http://headache.hungry.com/~seth/hifi/dirt.jpeg", + yTextureURL: "http://headache.hungry.com/~seth/hifi/grass.png", + zTextureURL: "http://headache.hungry.com/~seth/hifi/dirt.jpeg" }); - Entities.setAllVoxels(polyVoxId, 255); - for (var y = 8; y < 16; y++) { - for (var x = 0; x < 16; x++) { - for (var z = 0; z < 16; z++) { - Entities.setVoxel(polyVoxId, {x: x, y: y, z: z}, 0); - } - } + var AvatarPositionInVoxelCoords = Entities.worldCoordsToVoxelCoords(polyVoxID, MyAvatar.position); + // TODO -- how to find the avatar's feet? + var topY = Math.round(AvatarPositionInVoxelCoords.y) - 4; + Entities.setVoxelsInCuboid(polyVoxID, {x:0, y:0, z:0}, {x:16, y:topY, z:16}, 255); + + + ////////// + // stitch together the terrain with x/y/z NeighorID properties + ////////// + + // link neighbors to this plot + imXNNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:16, y:0, z:0})); + imYNNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:16, z:0})); + imZNNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:0, z:16})); + imXPNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:-16, y:0, z:0})); + imYPNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:-16, z:0})); + imZPNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:0, z:-16})); + + if (imXNNeighborFor) { + var properties = Entities.getEntityProperties(imXNNeighborFor); + properties.xNNeighborID = polyVoxID; + Entities.editEntity(imXNNeighborFor, properties); } + if (imYNNeighborFor) { + var properties = Entities.getEntityProperties(imYNNeighborFor); + properties.yNNeighborID = polyVoxID; + Entities.editEntity(imYNNeighborFor, properties); + } + if (imZNNeighborFor) { + var properties = Entities.getEntityProperties(imZNNeighborFor); + properties.zNNeighborID = polyVoxID; + Entities.editEntity(imZNNeighborFor, properties); + } + + if (imXPNeighborFor) { + var properties = Entities.getEntityProperties(imXPNeighborFor); + properties.xPNeighborID = polyVoxID; + Entities.editEntity(imXPNeighborFor, properties); + } + if (imYPNeighborFor) { + var properties = Entities.getEntityProperties(imYPNeighborFor); + properties.yPNeighborID = polyVoxID; + Entities.editEntity(imYPNeighborFor, properties); + } + if (imZPNeighborFor) { + var properties = Entities.getEntityProperties(imZPNeighborFor); + properties.zPNeighborID = polyVoxID; + Entities.editEntity(imZPNeighborFor, properties); + } + + + // link this plot to its neighbors + var properties = Entities.getEntityProperties(polyVoxID); + properties.xNNeighborID = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:-16, y:0, z:0})); + properties.yNNeighborID = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:-16, z:0})); + properties.zNNeighborID = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:0, z:-16})); + properties.xPNeighborID = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:16, y:0, z:0})); + properties.yPNeighborID = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:16, z:0})); + properties.zPNeighborID = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:0, z:16})); + Entities.editEntity(polyVoxID, properties); return true; } -function attemptVoxelChange(pickRayDir, intersection) { +function attemptVoxelChangeForEntity(entityID, pickRayDir, intersectionLocation) { - var properties = Entities.getEntityProperties(intersection.entityID); + var properties = Entities.getEntityProperties(entityID); if (properties.type != "PolyVox") { return false; } - if (addingVoxels == false && deletingVoxels == false) { + if (addingVoxels == false && deletingVoxels == false && addingSpheres == false && deletingSpheres == false) { return false; } - var voxelPosition = Entities.worldCoordsToVoxelCoords(intersection.entityID, intersection.intersection); - var pickRayDirInVoxelSpace = Entities.localCoordsToVoxelCoords(intersection.entityID, pickRayDir); + var voxelOrigin = Entities.worldCoordsToVoxelCoords(entityID, Vec3.subtract(intersectionLocation, pickRayDir)); + var voxelPosition = Entities.worldCoordsToVoxelCoords(entityID, intersectionLocation); + var pickRayDirInVoxelSpace = Vec3.subtract(voxelPosition, voxelOrigin); pickRayDirInVoxelSpace = Vec3.normalize(pickRayDirInVoxelSpace); var doAdd = addingVoxels; var doDelete = deletingVoxels; + var doAddSphere = addingSpheres; + var doDeleteSphere = deletingSpheres; if (controlHeld) { - doAdd = deletingVoxels; - doDelete = addingVoxels; + if (doAdd) { + doAdd = false; + doDelete = true; + } else if (doDelete) { + doDelete = false; + doAdd = true; + } else if (doAddSphere) { + doAddSphere = false; + doDeleteSphere = true; + } else if (doDeleteSphere) { + doDeleteSphere = false; + doAddSphere = true; + } } if (doDelete) { var toErasePosition = Vec3.sum(voxelPosition, Vec3.multiply(pickRayDirInVoxelSpace, 0.1)); - return Entities.setVoxel(intersection.entityID, floorVector(toErasePosition), 0); + return Entities.setVoxel(entityID, floorVector(toErasePosition), 0); } if (doAdd) { var toDrawPosition = Vec3.subtract(voxelPosition, Vec3.multiply(pickRayDirInVoxelSpace, 0.1)); - return Entities.setVoxel(intersection.entityID, floorVector(toDrawPosition), 255); + return Entities.setVoxel(entityID, floorVector(toDrawPosition), 255); + } + if (doDeleteSphere) { + var toErasePosition = intersectionLocation; + return Entities.setVoxelSphere(entityID, floorVector(toErasePosition), editSphereRadius, 0); + } + if (doAddSphere) { + var toDrawPosition = intersectionLocation; + return Entities.setVoxelSphere(entityID, floorVector(toDrawPosition), editSphereRadius, 255); } } + +function attemptVoxelChange(pickRayDir, intersection) { + + var ids; + + ids = Entities.findEntities(intersection.intersection, editSphereRadius + 1.0); + if (ids.indexOf(intersection.entityID) < 0) { + ids.push(intersection.entityID); + } + + var success = false; + for (var i = 0; i < ids.length; i++) { + var entityID = ids[i]; + success |= attemptVoxelChangeForEntity(entityID, pickRayDir, intersection.intersection) + } + return success; +} + + function mousePressEvent(event) { if (!event.isLeftButton) { return; diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 274c867ed7..5ffd0f8dec 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -547,14 +547,11 @@ void SkeletonModel::computeBoundingShape() { // compute the default transform of this joint const JointState& state = _rig->getJointState(i); - // HACK WORKAROUND: ignore joints that may have bad translation (e.g. have been flagged as such with zero radius) - if (state.getBoneRadius() > 0.0f) { - // Each joint contributes a sphere at its position - glm::vec3 axis(state.getBoneRadius()); - glm::vec3 jointPosition = state.getPosition(); - totalExtents.addPoint(jointPosition + axis); - totalExtents.addPoint(jointPosition - axis); - } + // Each joint contributes a sphere at its position + glm::vec3 axis(state.getBoneRadius()); + glm::vec3 jointPosition = state.getPosition(); + totalExtents.addPoint(jointPosition + axis); + totalExtents.addPoint(jointPosition - axis); } // compute bounding shape parameters diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp index de34451c83..ae3c1f29e2 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp @@ -78,7 +78,7 @@ void StereoDisplayPlugin::activate() { } void StereoDisplayPlugin::updateScreen() { - for (int i = 0; i < (int) _screenActions.size(); ++i) { + for (uint32_t i = 0; i < _screenActions.size(); ++i) { if (_screenActions[i]->isChecked()) { CONTAINER->setFullscreen(qApp->screens().at(i)); break; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 46c4986fa8..5ddee32f88 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -68,6 +68,19 @@ RenderablePolyVoxEntityItem::~RenderablePolyVoxEntityItem() { } +bool isEdged(PolyVoxEntityItem::PolyVoxSurfaceStyle surfaceStyle) { + switch (surfaceStyle) { + case PolyVoxEntityItem::SURFACE_CUBIC: + case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: + return false; + case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: + case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: + return true; + } + return false; +} + + void RenderablePolyVoxEntityItem::setVoxelData(QByteArray voxelData) { _voxelDataLock.lockForWrite(); if (_voxelData == voxelData) { @@ -88,10 +101,8 @@ void RenderablePolyVoxEntityItem::setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxel } // if we are switching to or from "edged" we need to force a resize of _volData. - bool wasEdged = (_voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC || - _voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES); - bool willBeEdged = (voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC || - voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES); + bool wasEdged = isEdged(_voxelSurfaceStyle); + bool willBeEdged = isEdged(voxelSurfaceStyle); if (wasEdged != willBeEdged) { _volDataLock.lockForWrite(); @@ -113,15 +124,10 @@ void RenderablePolyVoxEntityItem::setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxel glm::vec3 RenderablePolyVoxEntityItem::getSurfacePositionAdjustment() const { glm::vec3 scale = getDimensions() / _voxelVolumeSize; // meters / voxel-units - switch (_voxelSurfaceStyle) { - case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: - case PolyVoxEntityItem::SURFACE_CUBIC: - return scale / 2.0f; - case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: - case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: - return scale / -2.0f; + if (isEdged(_voxelSurfaceStyle)) { + return scale / -2.0f; } - return glm::vec3(0.0f, 0.0f, 0.0f); + return scale / 2.0f; } @@ -130,7 +136,7 @@ glm::mat4 RenderablePolyVoxEntityItem::voxelToLocalMatrix() const { glm::vec3 center = getCenterPosition(); glm::vec3 position = getPosition(); glm::vec3 positionToCenter = center - position; - positionToCenter -= getDimensions() * glm::vec3(0.5f, 0.5f, 0.5f) - getSurfacePositionAdjustment(); + positionToCenter -= getDimensions() * Vectors::HALF - getSurfacePositionAdjustment(); glm::mat4 centerToCorner = glm::translate(glm::mat4(), positionToCenter); glm::mat4 scaled = glm::scale(centerToCorner, scale); return scaled; @@ -195,6 +201,37 @@ bool RenderablePolyVoxEntityItem::setAll(uint8_t toValue) { } +bool RenderablePolyVoxEntityItem::setCuboid(const glm::vec3& lowPosition, const glm::vec3& cuboidSize, int toValue) { + bool result = false; + if (_locked) { + return result; + } + + int xLow = std::max(std::min((int)roundf(lowPosition.x), (int)roundf(_voxelVolumeSize.x) - 1), 0); + int yLow = std::max(std::min((int)roundf(lowPosition.y), (int)roundf(_voxelVolumeSize.y) - 1), 0); + int zLow = std::max(std::min((int)roundf(lowPosition.z), (int)roundf(_voxelVolumeSize.z) - 1), 0); + int xHigh = std::max(std::min(xLow + (int)roundf(cuboidSize.x), (int)roundf(_voxelVolumeSize.x)), xLow); + int yHigh = std::max(std::min(yLow + (int)roundf(cuboidSize.y), (int)roundf(_voxelVolumeSize.y)), yLow); + int zHigh = std::max(std::min(zLow + (int)roundf(cuboidSize.z), (int)roundf(_voxelVolumeSize.z)), zLow); + + _volDataLock.lockForWrite(); + _volDataDirty = true; + + for (int x = xLow; x < xHigh; x++) { + for (int y = yLow; y < yHigh; y++) { + for (int z = zLow; z < zHigh; z++) { + result |= setVoxelInternal(x, y, z, toValue); + } + } + } + _volDataLock.unlock(); + if (result) { + compressVolumeDataAndSendEditPacket(); + } + return result; +} + + bool RenderablePolyVoxEntityItem::setVoxelInVolume(glm::vec3 position, uint8_t toValue) { if (_locked) { @@ -213,7 +250,6 @@ bool RenderablePolyVoxEntityItem::setSphereInVolume(glm::vec3 center, float radi // This three-level for loop iterates over every voxel in the volume _volDataLock.lockForWrite(); - _volDataDirty = true; for (int z = 0; z < _voxelVolumeSize.z; z++) { for (int y = 0; y < _voxelVolumeSize.y; y++) { for (int x = 0; x < _voxelVolumeSize.x; x++) { @@ -228,20 +264,52 @@ bool RenderablePolyVoxEntityItem::setSphereInVolume(glm::vec3 center, float radi } } } - _volDataLock.unlock(); + if (result) { + _volDataDirty = true; + _volDataLock.unlock(); compressVolumeDataAndSendEditPacket(); + } else { + _volDataLock.unlock(); } return result; } bool RenderablePolyVoxEntityItem::setSphere(glm::vec3 centerWorldCoords, float radiusWorldCoords, uint8_t toValue) { - // glm::vec3 centerVoxelCoords = worldToVoxelCoordinates(centerWorldCoords); - glm::vec4 centerVoxelCoords = worldToVoxelMatrix() * glm::vec4(centerWorldCoords, 1.0f); - glm::vec3 scale = getDimensions() / _voxelVolumeSize; // meters / voxel-units - float scaleY = scale.y; - float radiusVoxelCoords = radiusWorldCoords / scaleY; - return setSphereInVolume(glm::vec3(centerVoxelCoords), radiusVoxelCoords, toValue); + bool result = false; + if (_locked) { + return result; + } + + glm::mat4 vtwMatrix = voxelToWorldMatrix(); + + // This three-level for loop iterates over every voxel in the volume + _volDataLock.lockForWrite(); + for (int z = 0; z < _voxelVolumeSize.z; z++) { + for (int y = 0; y < _voxelVolumeSize.y; y++) { + for (int x = 0; x < _voxelVolumeSize.x; x++) { + // Store our current position as a vector... + glm::vec4 pos(x + 0.5f, y + 0.5f, z + 0.5f, 1.0); // consider voxels cenetered on their coordinates + // convert to world coordinates + glm::vec3 worldPos = glm::vec3(vtwMatrix * pos); + // compute how far the current position is from the center of the volume + float fDistToCenter = glm::distance(worldPos, centerWorldCoords); + // If the current voxel is less than 'radius' units from the center then we set its value + if (fDistToCenter <= radiusWorldCoords) { + result |= setVoxelInternal(x, y, z, toValue); + } + } + } + } + + if (result) { + _volDataDirty = true; + _volDataLock.unlock(); + compressVolumeDataAndSendEditPacket(); + } else { + _volDataLock.unlock(); + } + return result; } class RaycastFunctor @@ -296,7 +364,6 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o glm::mat4 wtvMatrix = worldToVoxelMatrix(); glm::mat4 vtwMatrix = voxelToWorldMatrix(); - glm::mat4 vtlMatrix = voxelToLocalMatrix(); glm::vec3 normDirection = glm::normalize(direction); // the PolyVox ray intersection code requires a near and far point. @@ -304,67 +371,33 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o float distanceToEntity = glm::distance(origin, getPosition()); float largestDimension = glm::max(getDimensions().x, getDimensions().y, getDimensions().z) * 2.0f; glm::vec3 farPoint = origin + normDirection * (distanceToEntity + largestDimension); + glm::vec4 originInVoxel = wtvMatrix * glm::vec4(origin, 1.0f); glm::vec4 farInVoxel = wtvMatrix * glm::vec4(farPoint, 1.0f); - glm::vec4 result; + glm::vec4 directionInVoxel = glm::normalize(farInVoxel - originInVoxel); + + glm::vec4 result = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f); PolyVox::RaycastResult raycastResult = doRayCast(originInVoxel, farInVoxel, result); if (raycastResult == PolyVox::RaycastResults::Completed) { // the ray completed its path -- nothing was hit. return false; } - // set up ray tests against each face of the voxel. - glm::vec3 minXPosition = glm::vec3(vtwMatrix * (result + glm::vec4(0.0f, 0.5f, 0.5f, 0.0f))); - glm::vec3 maxXPosition = glm::vec3(vtwMatrix * (result + glm::vec4(1.0f, 0.5f, 0.5f, 0.0f))); - glm::vec3 minYPosition = glm::vec3(vtwMatrix * (result + glm::vec4(0.5f, 0.0f, 0.5f, 0.0f))); - glm::vec3 maxYPosition = glm::vec3(vtwMatrix * (result + glm::vec4(0.5f, 1.0f, 0.5f, 0.0f))); - glm::vec3 minZPosition = glm::vec3(vtwMatrix * (result + glm::vec4(0.5f, 0.5f, 0.0f, 0.0f))); - glm::vec3 maxZPosition = glm::vec3(vtwMatrix * (result + glm::vec4(0.5f, 0.5f, 1.0f, 0.0f))); + glm::vec3 result3 = glm::vec3(result); - glm::vec4 baseDimensions = glm::vec4(1.0, 1.0, 1.0, 0.0); - glm::vec3 worldDimensions = glm::vec3(vtlMatrix * baseDimensions); - glm::vec2 xDimensions = glm::vec2(worldDimensions.z, worldDimensions.y); - glm::vec2 yDimensions = glm::vec2(worldDimensions.x, worldDimensions.z); - glm::vec2 zDimensions = glm::vec2(worldDimensions.x, worldDimensions.y); + AABox voxelBox; + voxelBox += result3 - Vectors::HALF; + voxelBox += result3 + Vectors::HALF; - glm::quat vtwRotation = extractRotation(vtwMatrix); - glm::quat minXRotation = vtwRotation * glm::quat(glm::vec3(0.0f, PI_OVER_TWO, 0.0f)); - glm::quat maxXRotation = vtwRotation * glm::quat(glm::vec3(0.0f, PI_OVER_TWO, 0.0f)); - glm::quat minYRotation = vtwRotation * glm::quat(glm::vec3(PI_OVER_TWO, 0.0f, 0.0f)); - glm::quat maxYRotation = vtwRotation * glm::quat(glm::vec3(PI_OVER_TWO, 0.0f, 0.0f)); - glm::quat minZRotation = vtwRotation * glm::quat(glm::vec3(0.0f, 0.0f, 0.0f)); - glm::quat maxZRotation = vtwRotation * glm::quat(glm::vec3(0.0f, 0.0f, 0.0f)); + float voxelDistance; - float bestDx = FLT_MAX; - bool hit[ 6 ]; - float dx[ 6 ] = {FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX}; + bool hit = voxelBox.findRayIntersection(glm::vec3(originInVoxel), glm::vec3(directionInVoxel), voxelDistance, face); - hit[0] = findRayRectangleIntersection(origin, direction, minXRotation, minXPosition, xDimensions, dx[0]); - hit[1] = findRayRectangleIntersection(origin, direction, maxXRotation, maxXPosition, xDimensions, dx[1]); - hit[2] = findRayRectangleIntersection(origin, direction, minYRotation, minYPosition, yDimensions, dx[2]); - hit[3] = findRayRectangleIntersection(origin, direction, maxYRotation, maxYPosition, yDimensions, dx[3]); - hit[4] = findRayRectangleIntersection(origin, direction, minZRotation, minZPosition, zDimensions, dx[4]); - hit[5] = findRayRectangleIntersection(origin, direction, maxZRotation, maxZPosition, zDimensions, dx[5]); - - bool ok = false; - for (int i = 0; i < 6; i ++) { - if (hit[ i ] && dx[ i ] < bestDx) { - face = (BoxFace)i; - distance = dx[ i ]; - ok = true; - bestDx = dx[ i ]; - } - } - - if (!ok) { - // if the attempt to put the ray against one of the voxel-faces fails, just return the center - glm::vec4 intersectedWorldPosition = vtwMatrix * (result + vec4(0.5f, 0.5f, 0.5f, 0.0f)); - distance = glm::distance(glm::vec3(intersectedWorldPosition), origin); - face = BoxFace::MIN_X_FACE; - } - - return true; + glm::vec4 voxelIntersectionPoint = glm::vec4(glm::vec3(originInVoxel) + glm::vec3(directionInVoxel) * voxelDistance, 1.0); + glm::vec4 intersectionPoint = vtwMatrix * voxelIntersectionPoint; + distance = glm::distance(origin, glm::vec3(intersectionPoint)); + return hit; } @@ -380,7 +413,7 @@ PolyVox::RaycastResult RenderablePolyVoxEntityItem::doRayCast(glm::vec4 originIn _volDataLock.unlock(); // result is in voxel-space coordinates. - result = callback._result - glm::vec4(0.5f, 0.5f, 0.5f, 0.0f); + result = callback._result; return raycastResult; } @@ -402,21 +435,29 @@ bool RenderablePolyVoxEntityItem::isReadyToComputeShape() { } void RenderablePolyVoxEntityItem::computeShapeInfo(ShapeInfo& info) { - _shapeInfoLock.lockForRead(); + QReadLocker(&this->_shapeInfoLock); info = _shapeInfo; - _shapeInfoLock.unlock(); } void RenderablePolyVoxEntityItem::setXTextureURL(QString xTextureURL) { - PolyVoxEntityItem::setXTextureURL(xTextureURL); + if (xTextureURL != _xTextureURL) { + _xTexture.clear(); + PolyVoxEntityItem::setXTextureURL(xTextureURL); + } } void RenderablePolyVoxEntityItem::setYTextureURL(QString yTextureURL) { - PolyVoxEntityItem::setYTextureURL(yTextureURL); + if (yTextureURL != _yTextureURL) { + _yTexture.clear(); + PolyVoxEntityItem::setYTextureURL(yTextureURL); + } } void RenderablePolyVoxEntityItem::setZTextureURL(QString zTextureURL) { - PolyVoxEntityItem::setZTextureURL(zTextureURL); + if (zTextureURL != _zTextureURL) { + _zTexture.clear(); + PolyVoxEntityItem::setZTextureURL(zTextureURL); + } } void RenderablePolyVoxEntityItem::render(RenderArgs* args) { @@ -426,9 +467,12 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { _volDataLock.lockForRead(); if (_volDataDirty) { + _volDataLock.unlock(); getMesh(); + } else { + _volDataLock.unlock(); } - _volDataLock.unlock(); + _meshLock.lockForRead(); model::MeshPointer mesh = _mesh; @@ -543,23 +587,21 @@ namespace render { glm::vec3 RenderablePolyVoxEntityItem::voxelCoordsToWorldCoords(glm::vec3& voxelCoords) const { - return glm::vec3(voxelToWorldMatrix() * glm::vec4(voxelCoords, 1.0f)); + glm::vec3 adjustedCoords; + if (isEdged(_voxelSurfaceStyle)) { + adjustedCoords = voxelCoords + Vectors::HALF; + } else { + adjustedCoords = voxelCoords - Vectors::HALF; + } + return glm::vec3(voxelToWorldMatrix() * glm::vec4(adjustedCoords, 1.0f)); } glm::vec3 RenderablePolyVoxEntityItem::worldCoordsToVoxelCoords(glm::vec3& worldCoords) const { glm::vec3 result = glm::vec3(worldToVoxelMatrix() * glm::vec4(worldCoords, 1.0f)); - switch (_voxelSurfaceStyle) { - case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: - case PolyVoxEntityItem::SURFACE_CUBIC: - result += glm::vec3(0.5f, 0.5f, 0.5f); - break; - case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: - case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: - result -= glm::vec3(0.5f, 0.5f, 0.5f); - break; + if (isEdged(_voxelSurfaceStyle)) { + return result - Vectors::HALF; } - - return result; + return result + Vectors::HALF; } glm::vec3 RenderablePolyVoxEntityItem::voxelCoordsToLocalCoords(glm::vec3& voxelCoords) const { @@ -585,8 +627,7 @@ void RenderablePolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) } _onCount = 0; - if (_voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC || - _voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES) { + if (isEdged(_voxelSurfaceStyle)) { // with _EDGED_ we maintain an extra box of voxels around those that the user asked for. This // changes how the surface extractor acts -- mainly it becomes impossible to have holes in the // generated mesh. The non _EDGED_ modes will leave holes in the mesh at the edges of the @@ -598,9 +639,11 @@ void RenderablePolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) _volData = new PolyVox::SimpleVolume(PolyVox::Region(lowCorner, highCorner)); } else { PolyVox::Vector3DInt32 lowCorner(0, 0, 0); - PolyVox::Vector3DInt32 highCorner(_voxelVolumeSize.x - 1, // -1 because these corners are inclusive - _voxelVolumeSize.y - 1, - _voxelVolumeSize.z - 1); + // these should each have -1 after them, but if we leave layers on the upper-axis faces, + // they act more like I expect. + PolyVox::Vector3DInt32 highCorner(_voxelVolumeSize.x, + _voxelVolumeSize.y, + _voxelVolumeSize.z); _volData = new PolyVox::SimpleVolume(PolyVox::Region(lowCorner, highCorner)); } @@ -613,35 +656,27 @@ void RenderablePolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) bool RenderablePolyVoxEntityItem::inUserBounds(const PolyVox::SimpleVolume* vol, PolyVoxEntityItem::PolyVoxSurfaceStyle surfaceStyle, - int x, int y, int z) { + int x, int y, int z) const { // x, y, z are in user voxel-coords, not adjusted-for-edge voxel-coords. - switch (surfaceStyle) { - case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: - case PolyVoxEntityItem::SURFACE_CUBIC: - if (x < 0 || y < 0 || z < 0 || - x >= vol->getWidth() || y >= vol->getHeight() || z >= vol->getDepth()) { - return false; - } - return true; - - case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: - case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: - if (x < 0 || y < 0 || z < 0 || - x >= vol->getWidth() - 2 || y >= vol->getHeight() - 2 || z >= vol->getDepth() - 2) { - return false; - } - return true; + if (isEdged(surfaceStyle)) { + if (x < 0 || y < 0 || z < 0 || + x >= vol->getWidth() - 2 || y >= vol->getHeight() - 2 || z >= vol->getDepth() - 2) { + return false; + } + return true; + } else { + if (x < 0 || y < 0 || z < 0 || + x >= vol->getWidth() || y >= vol->getHeight() || z >= vol->getDepth()) { + return false; + } + return true; } - - return false; } uint8_t RenderablePolyVoxEntityItem::getVoxel(int x, int y, int z) { - _volDataLock.lockForRead(); - auto result = getVoxelInternal(x, y, z); - _volDataLock.unlock(); - return result; + QReadLocker(&this->_volDataLock); + return getVoxelInternal(x, y, z); } @@ -650,19 +685,13 @@ uint8_t RenderablePolyVoxEntityItem::getVoxelInternal(int x, int y, int z) { return 0; } - // if _voxelSurfaceStyle is SURFACE_EDGED_CUBIC, we maintain an extra layer of + // if _voxelSurfaceStyle is *_EDGED_*, we maintain an extra layer of // voxels all around the requested voxel space. Having the empty voxels around // the edges changes how the surface extractor behaves. - - uint8_t result; - if (_voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC || - _voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES) { - result = _volData->getVoxelAt(x + 1, y + 1, z + 1); - } else { - result = _volData->getVoxelAt(x, y, z); + if (isEdged(_voxelSurfaceStyle)) { + return _volData->getVoxelAt(x + 1, y + 1, z + 1); } - - return result; + return _volData->getVoxelAt(x, y, z); } @@ -675,9 +704,7 @@ bool RenderablePolyVoxEntityItem::setVoxelInternal(int x, int y, int z, uint8_t result = updateOnCount(x, y, z, toValue); - assert(_volData); - if (_voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC || - _voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES) { + if (isEdged(_voxelSurfaceStyle)) { _volData->setVoxelAt(x + 1, y + 1, z + 1, toValue); } else { _volData->setVoxelAt(x, y, z, toValue); @@ -710,13 +737,11 @@ bool RenderablePolyVoxEntityItem::updateOnCount(int x, int y, int z, uint8_t toV return false; } - void RenderablePolyVoxEntityItem::decompressVolumeData() { _threadRunning.acquire(); QtConcurrent::run(this, &RenderablePolyVoxEntityItem::decompressVolumeDataAsync); } - // take compressed data and expand it into _volData. void RenderablePolyVoxEntityItem::decompressVolumeDataAsync() { _voxelDataLock.lockForRead(); @@ -777,7 +802,6 @@ void RenderablePolyVoxEntityItem::compressVolumeDataAndSendEditPacket() { QtConcurrent::run(this, &RenderablePolyVoxEntityItem::compressVolumeDataAndSendEditPacketAsync); } - // compress the data in _volData and save the results. The compressed form is used during // saves to disk and for transmission over the wire void RenderablePolyVoxEntityItem::compressVolumeDataAndSendEditPacketAsync() { @@ -850,23 +874,157 @@ void RenderablePolyVoxEntityItem::getMesh() { QtConcurrent::run(this, &RenderablePolyVoxEntityItem::getMeshAsync); } +void RenderablePolyVoxEntityItem::clearOutOfDateNeighbors() { + if (_xNNeighborID != UNKNOWN_ENTITY_ID) { + EntityItemPointer currentXNNeighbor = _xNNeighbor.lock(); + if (currentXNNeighbor && currentXNNeighbor->getID() != _xNNeighborID) { + _xNNeighbor.reset(); + } + } + if (_yNNeighborID != UNKNOWN_ENTITY_ID) { + EntityItemPointer currentYNNeighbor = _yNNeighbor.lock(); + if (currentYNNeighbor && currentYNNeighbor->getID() != _yNNeighborID) { + _yNNeighbor.reset(); + } + } + if (_zNNeighborID != UNKNOWN_ENTITY_ID) { + EntityItemPointer currentZNNeighbor = _zNNeighbor.lock(); + if (currentZNNeighbor && currentZNNeighbor->getID() != _zNNeighborID) { + _zNNeighbor.reset(); + } + } + + if (_xPNeighborID != UNKNOWN_ENTITY_ID) { + EntityItemPointer currentXPNeighbor = _xPNeighbor.lock(); + if (currentXPNeighbor && currentXPNeighbor->getID() != _xPNeighborID) { + _xPNeighbor.reset(); + } + } + if (_yPNeighborID != UNKNOWN_ENTITY_ID) { + EntityItemPointer currentYPNeighbor = _yPNeighbor.lock(); + if (currentYPNeighbor && currentYPNeighbor->getID() != _yPNeighborID) { + _yPNeighbor.reset(); + } + } + if (_zPNeighborID != UNKNOWN_ENTITY_ID) { + EntityItemPointer currentZPNeighbor = _zPNeighbor.lock(); + if (currentZPNeighbor && currentZPNeighbor->getID() != _zPNeighborID) { + _zPNeighbor.reset(); + } + } + +} + +void RenderablePolyVoxEntityItem::cacheNeighbors() { + clearOutOfDateNeighbors(); + EntityTreeElement* element = getElement(); + EntityTree* tree = element ? element->getTree() : nullptr; + if (!tree) { + return; + } + + if (_xNNeighborID != UNKNOWN_ENTITY_ID && _xNNeighbor.expired()) { + _xNNeighbor = tree->findEntityByID(_xNNeighborID); + } + if (_yNNeighborID != UNKNOWN_ENTITY_ID && _yNNeighbor.expired()) { + _yNNeighbor = tree->findEntityByID(_yNNeighborID); + } + if (_zNNeighborID != UNKNOWN_ENTITY_ID && _zNNeighbor.expired()) { + _zNNeighbor = tree->findEntityByID(_zNNeighborID); + } + + if (_xPNeighborID != UNKNOWN_ENTITY_ID && _xPNeighbor.expired()) { + _xPNeighbor = tree->findEntityByID(_xPNeighborID); + } + if (_yPNeighborID != UNKNOWN_ENTITY_ID && _yPNeighbor.expired()) { + _yPNeighbor = tree->findEntityByID(_yPNeighborID); + } + if (_zPNeighborID != UNKNOWN_ENTITY_ID && _zPNeighbor.expired()) { + _zPNeighbor = tree->findEntityByID(_zPNeighborID); + } + +} + +void RenderablePolyVoxEntityItem::copyUpperEdgesFromNeighbors() { + if (_voxelSurfaceStyle != PolyVoxEntityItem::SURFACE_MARCHING_CUBES) { + return; + } + + EntityItemPointer currentXPNeighbor = _xPNeighbor.lock(); + EntityItemPointer currentYPNeighbor = _yPNeighbor.lock(); + EntityItemPointer currentZPNeighbor = _zPNeighbor.lock(); + + if (currentXPNeighbor) { + auto polyVoxXPNeighbor = std::dynamic_pointer_cast(currentXPNeighbor); + if (polyVoxXPNeighbor->getVoxelVolumeSize() == _voxelVolumeSize) { + for (int y = 0; y < _volData->getHeight(); y++) { + for (int z = 0; z < _volData->getDepth(); z++) { + uint8_t neighborValue = polyVoxXPNeighbor->getVoxel(0, y, z); + _volData->setVoxelAt(_volData->getWidth() - 1, y, z, neighborValue); + } + } + } + } + + if (currentYPNeighbor) { + auto polyVoxYPNeighbor = std::dynamic_pointer_cast(currentYPNeighbor); + if (polyVoxYPNeighbor->getVoxelVolumeSize() == _voxelVolumeSize) { + for (int x = 0; x < _volData->getWidth(); x++) { + for (int z = 0; z < _volData->getDepth(); z++) { + uint8_t neighborValue = polyVoxYPNeighbor->getVoxel(x, 0, z); + _volData->setVoxelAt(x, _volData->getWidth() - 1, z, neighborValue); + } + } + } + } + + if (currentZPNeighbor) { + auto polyVoxZPNeighbor = std::dynamic_pointer_cast(currentZPNeighbor); + if (polyVoxZPNeighbor->getVoxelVolumeSize() == _voxelVolumeSize) { + for (int x = 0; x < _volData->getWidth(); x++) { + for (int y = 0; y < _volData->getHeight(); y++) { + uint8_t neighborValue = polyVoxZPNeighbor->getVoxel(x, y, 0); + _volData->setVoxelAt(x, y, _volData->getDepth() - 1, neighborValue); + } + } + } + } +} void RenderablePolyVoxEntityItem::getMeshAsync() { model::MeshPointer mesh(new model::Mesh()); + cacheNeighbors(); + // A mesh object to hold the result of surface extraction PolyVox::SurfaceMesh polyVoxMesh; _volDataLock.lockForRead(); + if (!_volData) { + _volDataLock.unlock(); + return; + } + copyUpperEdgesFromNeighbors(); + switch (_voxelSurfaceStyle) { - case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: + case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: { + PolyVox::MarchingCubesSurfaceExtractor> surfaceExtractor + (_volData, _volData->getEnclosingRegion(), &polyVoxMesh); + surfaceExtractor.execute(); + break; + } case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: { PolyVox::MarchingCubesSurfaceExtractor> surfaceExtractor (_volData, _volData->getEnclosingRegion(), &polyVoxMesh); surfaceExtractor.execute(); break; } - case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: + case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: { + PolyVox::CubicSurfaceExtractorWithNormals> surfaceExtractor + (_volData, _volData->getEnclosingRegion(), &polyVoxMesh); + surfaceExtractor.execute(); + break; + } case PolyVoxEntityItem::SURFACE_CUBIC: { PolyVox::CubicSurfaceExtractorWithNormals> surfaceExtractor (_volData, _volData->getEnclosingRegion(), &polyVoxMesh); @@ -908,7 +1066,7 @@ void RenderablePolyVoxEntityItem::getMeshAsync() { _meshLock.unlock(); _volDataDirty = false; _volDataLock.unlock(); - + bonkNeighbors(); _threadRunning.release(); } @@ -925,7 +1083,7 @@ void RenderablePolyVoxEntityItem::computeShapeInfoWorkerAsync() { if (_voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_MARCHING_CUBES || _voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES) { - /* pull each triangle in the mesh into a polyhedron which can be collided with */ + // pull each triangle in the mesh into a polyhedron which can be collided with unsigned int i = 0; _meshLock.lockForRead(); @@ -972,10 +1130,16 @@ void RenderablePolyVoxEntityItem::computeShapeInfoWorkerAsync() { } else { unsigned int i = 0; + _volDataLock.lockForRead(); + if (!_volData) { + _volDataLock.unlock(); + return; + } + for (int z = 0; z < _voxelVolumeSize.z; z++) { for (int y = 0; y < _voxelVolumeSize.y; y++) { for (int x = 0; x < _voxelVolumeSize.x; x++) { - if (getVoxel(x, y, z) > 0) { + if (getVoxelInternal(x, y, z) > 0) { if ((x > 0 && getVoxel(x - 1, y, z) > 0) && (y > 0 && getVoxel(x, y - 1, z) > 0) && @@ -1033,6 +1197,7 @@ void RenderablePolyVoxEntityItem::computeShapeInfoWorkerAsync() { } } } + _volDataLock.unlock(); } if (points.isEmpty()) { @@ -1056,3 +1221,93 @@ void RenderablePolyVoxEntityItem::computeShapeInfoWorkerAsync() { _threadRunning.release(); return; } + + +void RenderablePolyVoxEntityItem::setXNNeighborID(const EntityItemID& xNNeighborID) { + if (xNNeighborID != _xNNeighborID) { + PolyVoxEntityItem::setXNNeighborID(xNNeighborID); + cacheNeighbors(); + EntityItemPointer currentXNNeighbor = _xNNeighbor.lock(); + if (currentXNNeighbor && currentXNNeighbor->getType() == EntityTypes::PolyVox) { + auto polyVoxXNNeighbor = std::dynamic_pointer_cast(currentXNNeighbor); + polyVoxXNNeighbor->setXPNeighborID(_id); + polyVoxXNNeighbor->rebakeMesh(); + } + } +} + +void RenderablePolyVoxEntityItem::setYNNeighborID(const EntityItemID& yNNeighborID) { + if (yNNeighborID != _yNNeighborID) { + PolyVoxEntityItem::setYNNeighborID(yNNeighborID); + cacheNeighbors(); + EntityItemPointer currentYNNeighbor = _yNNeighbor.lock(); + if (currentYNNeighbor && currentYNNeighbor->getType() == EntityTypes::PolyVox) { + auto polyVoxYNNeighbor = std::dynamic_pointer_cast(currentYNNeighbor); + polyVoxYNNeighbor->setYPNeighborID(_id); + polyVoxYNNeighbor->rebakeMesh(); + } + } +} + +void RenderablePolyVoxEntityItem::setZNNeighborID(const EntityItemID& zNNeighborID) { + if (zNNeighborID != _zNNeighborID) { + PolyVoxEntityItem::setZNNeighborID(zNNeighborID); + cacheNeighbors(); + EntityItemPointer currentZNNeighbor = _yNNeighbor.lock(); + if (currentZNNeighbor && currentZNNeighbor->getType() == EntityTypes::PolyVox) { + auto polyVoxZNNeighbor = std::dynamic_pointer_cast(currentZNNeighbor); + polyVoxZNNeighbor->setZPNeighborID(_id); + polyVoxZNNeighbor->rebakeMesh(); + } + } +} + + +void RenderablePolyVoxEntityItem::setXPNeighborID(const EntityItemID& xPNeighborID) { + if (xPNeighborID != _xPNeighborID) { + PolyVoxEntityItem::setXPNeighborID(xPNeighborID); + rebakeMesh(); + } +} + +void RenderablePolyVoxEntityItem::setYPNeighborID(const EntityItemID& yPNeighborID) { + if (yPNeighborID != _yPNeighborID) { + PolyVoxEntityItem::setYPNeighborID(yPNeighborID); + rebakeMesh(); + } +} + +void RenderablePolyVoxEntityItem::setZPNeighborID(const EntityItemID& zPNeighborID) { + if (zPNeighborID != _zPNeighborID) { + PolyVoxEntityItem::setZPNeighborID(zPNeighborID); + rebakeMesh(); + } +} + + +void RenderablePolyVoxEntityItem::rebakeMesh() { + QReadLocker(&this->_volDataLock); + _volDataDirty = true; +} + +void RenderablePolyVoxEntityItem::bonkNeighbors() { + clearOutOfDateNeighbors(); + cacheNeighbors(); + + EntityItemPointer currentXNNeighbor = _xNNeighbor.lock(); + EntityItemPointer currentYNNeighbor = _yNNeighbor.lock(); + EntityItemPointer currentZNNeighbor = _zNNeighbor.lock(); + + if (currentXNNeighbor && currentXNNeighbor->getType() == EntityTypes::PolyVox) { + auto polyVoxXNNeighbor = std::dynamic_pointer_cast(currentXNNeighbor); + polyVoxXNNeighbor->rebakeMesh(); + } + if (currentYNNeighbor && currentYNNeighbor->getType() == EntityTypes::PolyVox) { + auto polyVoxYNNeighbor = std::dynamic_pointer_cast(currentYNNeighbor); + polyVoxYNNeighbor->rebakeMesh(); + } + if (currentZNNeighbor && currentZNNeighbor->getType() == EntityTypes::PolyVox) { + auto polyVoxZNNeighbor = std::dynamic_pointer_cast(currentZNNeighbor); + polyVoxZNNeighbor->rebakeMesh(); + } +} diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index 110e8f8ab4..01578b5e58 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -93,6 +93,7 @@ public: // coords are in world-space virtual bool setSphere(glm::vec3 center, float radius, uint8_t toValue); virtual bool setAll(uint8_t toValue); + virtual bool setCuboid(const glm::vec3& lowPosition, const glm::vec3& cuboidSize, int toValue); virtual void setXTextureURL(QString xTextureURL); virtual void setYTextureURL(QString yTextureURL); @@ -105,6 +106,16 @@ public: std::shared_ptr scene, render::PendingChanges& pendingChanges); + virtual void setXNNeighborID(const EntityItemID& xNNeighborID); + virtual void setYNNeighborID(const EntityItemID& yNNeighborID); + virtual void setZNNeighborID(const EntityItemID& zNNeighborID); + + virtual void setXPNeighborID(const EntityItemID& xPNeighborID); + virtual void setYPNeighborID(const EntityItemID& yPNeighborID); + virtual void setZPNeighborID(const EntityItemID& zPNeighborID); + + virtual void rebakeMesh(); + private: // The PolyVoxEntityItem class has _voxelData which contains dimensions and compressed voxel data. The dimensions // may not match _voxelVolumeSize. @@ -130,7 +141,7 @@ private: int _onCount; // how many non-zero voxels are in _volData bool inUserBounds(const PolyVox::SimpleVolume* vol, PolyVoxEntityItem::PolyVoxSurfaceStyle surfaceStyle, - int x, int y, int z); + int x, int y, int z) const; uint8_t getVoxelInternal(int x, int y, int z); bool setVoxelInternal(int x, int y, int z, uint8_t toValue); bool updateOnCount(int x, int y, int z, uint8_t toValue); @@ -147,6 +158,18 @@ private: void computeShapeInfoWorkerAsync(); QSemaphore _threadRunning{1}; + + // these are cached lookups of _xNNeighborID, _yNNeighborID, _zNNeighborID, _xPNeighborID, _yPNeighborID, _zPNeighborID + EntityItemWeakPointer _xNNeighbor; // neighor found by going along negative X axis + EntityItemWeakPointer _yNNeighbor; + EntityItemWeakPointer _zNNeighbor; + EntityItemWeakPointer _xPNeighbor; // neighor found by going along positive X axis + EntityItemWeakPointer _yPNeighbor; + EntityItemWeakPointer _zPNeighbor; + void clearOutOfDateNeighbors(); + void cacheNeighbors(); + void copyUpperEdgesFromNeighbors(); + void bonkNeighbors(); }; diff --git a/libraries/entities-renderer/src/polyvox.slf b/libraries/entities-renderer/src/polyvox.slf index 5772a93f7d..c694630770 100644 --- a/libraries/entities-renderer/src/polyvox.slf +++ b/libraries/entities-renderer/src/polyvox.slf @@ -12,16 +12,12 @@ // <@include gpu/Inputs.slh@> - -layout(location = 0) out vec4 _fragColor0; -layout(location = 1) out vec4 _fragColor1; -layout(location = 2) out vec4 _fragColor2; - <@include model/Material.slh@> +<@include DeferredBufferWrite.slh@> in vec3 _normal; in vec4 _position; -in vec4 _inPosition; +in vec4 _worldPosition; uniform sampler2D xMap; uniform sampler2D yMap; @@ -29,12 +25,12 @@ uniform sampler2D zMap; uniform vec3 voxelVolumeSize; void main(void) { - vec3 worldNormal = cross(dFdy(_inPosition.xyz), dFdx(_inPosition.xyz)); + vec3 worldNormal = cross(dFdy(_worldPosition.xyz), dFdx(_worldPosition.xyz)); worldNormal = normalize(worldNormal); - float inPositionX = (_inPosition.x - 0.5) / voxelVolumeSize.x; - float inPositionY = (_inPosition.y - 0.5) / voxelVolumeSize.y; - float inPositionZ = (_inPosition.z - 0.5) / voxelVolumeSize.z; + float inPositionX = (_worldPosition.x - 0.5) / voxelVolumeSize.x; + float inPositionY = (_worldPosition.y - 0.5) / voxelVolumeSize.y; + float inPositionZ = (_worldPosition.z - 0.5) / voxelVolumeSize.z; vec4 xyDiffuse = texture(xMap, vec2(-inPositionX, -inPositionY)); vec4 xzDiffuse = texture(yMap, vec2(-inPositionX, inPositionZ)); @@ -43,12 +39,7 @@ void main(void) { vec3 xyDiffuseScaled = xyDiffuse.rgb * abs(worldNormal.z); vec3 xzDiffuseScaled = xzDiffuse.rgb * abs(worldNormal.y); vec3 yzDiffuseScaled = yzDiffuse.rgb * abs(worldNormal.x); - vec4 diffuse = vec4(xyDiffuseScaled + xzDiffuseScaled + yzDiffuseScaled, 1.0); - Material mat = getMaterial(); - - _fragColor0 = vec4(diffuse.rgb, 0.0); - _fragColor1 = vec4(_normal, 1.0); - _fragColor2 = vec4(getMaterialSpecular(mat), getMaterialShininess(mat) / 128.0); + packDeferredFragment(_normal, 0.0, vec3(diffuse), vec3(0.01, 0.01, 0.01), 10.0); } diff --git a/libraries/entities-renderer/src/polyvox.slv b/libraries/entities-renderer/src/polyvox.slv index 0074993c80..eb8d264a1b 100644 --- a/libraries/entities-renderer/src/polyvox.slv +++ b/libraries/entities-renderer/src/polyvox.slv @@ -17,7 +17,7 @@ <$declareStandardTransform()$> out vec4 _position; -out vec4 _inPosition; +out vec4 _worldPosition; out vec3 _normal; void main(void) { @@ -26,5 +26,5 @@ void main(void) { TransformObject obj = getTransformObject(); <$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$> <$transformModelToEyeDir(cam, obj, inNormal.xyz, _normal)$> - _inPosition = inPosition; + _worldPosition = inPosition; } diff --git a/libraries/entities/src/EntityItemID.h b/libraries/entities/src/EntityItemID.h index 7f55005a17..765082ef0b 100644 --- a/libraries/entities/src/EntityItemID.h +++ b/libraries/entities/src/EntityItemID.h @@ -32,26 +32,8 @@ public: QScriptValue toScriptValue(QScriptEngine* engine) const; bool isInvalidID() const { return *this == UNKNOWN_ENTITY_ID; } - - // QUuid id; }; -// inline bool operator<(const EntityItemID& a, const EntityItemID& b) { -// return a.id < b.id; -// } - -// inline bool operator==(const EntityItemID& a, const EntityItemID& b) { -// return a.id == b.id; -// } - -// inline bool operator!=(const EntityItemID& a, const EntityItemID& b) { -// return !(a == b); -// } - -// inline uint qHash(const EntityItemID& a, uint seed) { -// return qHash(a.id, seed); -// } - inline QDebug operator<<(QDebug debug, const EntityItemID& id) { debug << "[entity-id:" << id.toString() << "]"; return debug; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index c6c02f248c..89284b2123 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -109,6 +109,13 @@ CONSTRUCT_PROPERTY(strokeWidths, QVector()), CONSTRUCT_PROPERTY(xTextureURL, ""), CONSTRUCT_PROPERTY(yTextureURL, ""), CONSTRUCT_PROPERTY(zTextureURL, ""), +CONSTRUCT_PROPERTY(xNNeighborID, UNKNOWN_ENTITY_ID), +CONSTRUCT_PROPERTY(yNNeighborID, UNKNOWN_ENTITY_ID), +CONSTRUCT_PROPERTY(zNNeighborID, UNKNOWN_ENTITY_ID), +CONSTRUCT_PROPERTY(xPNeighborID, UNKNOWN_ENTITY_ID), +CONSTRUCT_PROPERTY(yPNeighborID, UNKNOWN_ENTITY_ID), +CONSTRUCT_PROPERTY(zPNeighborID, UNKNOWN_ENTITY_ID), + _id(UNKNOWN_ENTITY_ID), _idSet(false), @@ -377,6 +384,12 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_X_TEXTURE_URL, xTextureURL); CHECK_PROPERTY_CHANGE(PROP_Y_TEXTURE_URL, yTextureURL); CHECK_PROPERTY_CHANGE(PROP_Z_TEXTURE_URL, zTextureURL); + CHECK_PROPERTY_CHANGE(PROP_X_N_NEIGHBOR_ID, xNNeighborID); + CHECK_PROPERTY_CHANGE(PROP_Y_N_NEIGHBOR_ID, yNNeighborID); + CHECK_PROPERTY_CHANGE(PROP_Z_N_NEIGHBOR_ID, zNNeighborID); + CHECK_PROPERTY_CHANGE(PROP_X_P_NEIGHBOR_ID, xPNeighborID); + CHECK_PROPERTY_CHANGE(PROP_Y_P_NEIGHBOR_ID, yPNeighborID); + CHECK_PROPERTY_CHANGE(PROP_Z_P_NEIGHBOR_ID, zPNeighborID); changedProperties += _stage.getChangedProperties(); changedProperties += _atmosphere.getChangedProperties(); @@ -521,6 +534,14 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(yTextureURL); COPY_PROPERTY_TO_QSCRIPTVALUE(zTextureURL); + COPY_PROPERTY_TO_QSCRIPTVALUE(xNNeighborID); + COPY_PROPERTY_TO_QSCRIPTVALUE(yNNeighborID); + COPY_PROPERTY_TO_QSCRIPTVALUE(zNNeighborID); + + COPY_PROPERTY_TO_QSCRIPTVALUE(xPNeighborID); + COPY_PROPERTY_TO_QSCRIPTVALUE(yPNeighborID); + COPY_PROPERTY_TO_QSCRIPTVALUE(zPNeighborID); + return properties; } @@ -620,6 +641,14 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(yTextureURL, QString, setYTextureURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(zTextureURL, QString, setZTextureURL); + COPY_PROPERTY_FROM_QSCRIPTVALUE(xNNeighborID, EntityItemID, setXNNeighborID); + COPY_PROPERTY_FROM_QSCRIPTVALUE(yNNeighborID, EntityItemID, setYNNeighborID); + COPY_PROPERTY_FROM_QSCRIPTVALUE(zNNeighborID, EntityItemID, setZNNeighborID); + + COPY_PROPERTY_FROM_QSCRIPTVALUE(xPNeighborID, EntityItemID, setXPNeighborID); + COPY_PROPERTY_FROM_QSCRIPTVALUE(yPNeighborID, EntityItemID, setYPNeighborID); + COPY_PROPERTY_FROM_QSCRIPTVALUE(zPNeighborID, EntityItemID, setZPNeighborID); + _lastEdited = usecTimestampNow(); } @@ -852,6 +881,12 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType::Value command, Ent APPEND_ENTITY_PROPERTY(PROP_X_TEXTURE_URL, properties.getXTextureURL()); APPEND_ENTITY_PROPERTY(PROP_Y_TEXTURE_URL, properties.getYTextureURL()); APPEND_ENTITY_PROPERTY(PROP_Z_TEXTURE_URL, properties.getZTextureURL()); + APPEND_ENTITY_PROPERTY(PROP_X_N_NEIGHBOR_ID, properties.getXNNeighborID()); + APPEND_ENTITY_PROPERTY(PROP_Y_N_NEIGHBOR_ID, properties.getYNNeighborID()); + APPEND_ENTITY_PROPERTY(PROP_Z_N_NEIGHBOR_ID, properties.getZNNeighborID()); + APPEND_ENTITY_PROPERTY(PROP_X_P_NEIGHBOR_ID, properties.getXPNeighborID()); + APPEND_ENTITY_PROPERTY(PROP_Y_P_NEIGHBOR_ID, properties.getYPNeighborID()); + APPEND_ENTITY_PROPERTY(PROP_Z_P_NEIGHBOR_ID, properties.getZPNeighborID()); } if (properties.getType() == EntityTypes::Line) { @@ -1115,6 +1150,12 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_X_TEXTURE_URL, QString, setXTextureURL); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_Y_TEXTURE_URL, QString, setYTextureURL); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_Z_TEXTURE_URL, QString, setZTextureURL); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_X_N_NEIGHBOR_ID, EntityItemID, setXNNeighborID); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_Y_N_NEIGHBOR_ID, EntityItemID, setYNNeighborID); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_Z_N_NEIGHBOR_ID, EntityItemID, setZNNeighborID); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_X_P_NEIGHBOR_ID, EntityItemID, setXPNeighborID); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_Y_P_NEIGHBOR_ID, EntityItemID, setYPNeighborID); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_Z_P_NEIGHBOR_ID, EntityItemID, setZPNeighborID); } if (properties.getType() == EntityTypes::Line) { @@ -1248,13 +1289,21 @@ void EntityItemProperties::markAllChanged() { _descriptionChanged = true; _faceCameraChanged = true; _actionDataChanged = true; - + _normalsChanged = true; _strokeWidthsChanged = true; _xTextureURLChanged = true; _yTextureURLChanged = true; _zTextureURLChanged = true; + + _xNNeighborIDChanged = true; + _yNNeighborIDChanged = true; + _zNNeighborIDChanged = true; + + _xPNeighborIDChanged = true; + _yPNeighborIDChanged = true; + _zPNeighborIDChanged = true; } /// The maximum bounding cube for the entity, independent of it's rotation. diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 6d95faa9b1..942dc3edb1 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -161,6 +161,12 @@ public: DEFINE_PROPERTY_REF(PROP_X_TEXTURE_URL, XTextureURL, xTextureURL, QString); DEFINE_PROPERTY_REF(PROP_Y_TEXTURE_URL, YTextureURL, yTextureURL, QString); DEFINE_PROPERTY_REF(PROP_Z_TEXTURE_URL, ZTextureURL, zTextureURL, QString); + DEFINE_PROPERTY_REF(PROP_X_N_NEIGHBOR_ID, XNNeighborID, xNNeighborID, EntityItemID); + DEFINE_PROPERTY_REF(PROP_Y_N_NEIGHBOR_ID, YNNeighborID, yNNeighborID, EntityItemID); + DEFINE_PROPERTY_REF(PROP_Z_N_NEIGHBOR_ID, ZNNeighborID, zNNeighborID, EntityItemID); + DEFINE_PROPERTY_REF(PROP_X_P_NEIGHBOR_ID, XPNeighborID, xPNeighborID, EntityItemID); + DEFINE_PROPERTY_REF(PROP_Y_P_NEIGHBOR_ID, YPNeighborID, yPNeighborID, EntityItemID); + DEFINE_PROPERTY_REF(PROP_Z_P_NEIGHBOR_ID, ZPNeighborID, zPNeighborID, EntityItemID); static QString getBackgroundModeString(BackgroundMode mode); @@ -327,6 +333,12 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, XTextureURL, xTextureURL, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, YTextureURL, yTextureURL, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, ZTextureURL, zTextureURL, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, XNNeighborID, xNNeighborID, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, YNNeighborID, yNNeighborID, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, ZNNeighborID, zNNeighborID, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, XPNeighborID, xPNeighborID, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, YPNeighborID, yPNeighborID, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, ZPNeighborID, zPNeighborID, ""); properties.getStage().debugDump(); properties.getAtmosphere().debugDump(); diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index e48be1da8c..a3e31024d1 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -14,6 +14,8 @@ #ifndef hifi_EntityItemPropertiesMacros_h #define hifi_EntityItemPropertiesMacros_h +#include "EntityItemID.h" + #define APPEND_ENTITY_PROPERTY(P,V) \ if (requestedProperties.getHasProperty(P)) { \ LevelDetails propertyLevel = packetData->startLevel(); \ @@ -106,6 +108,9 @@ inline QScriptValue convertScriptValue(QScriptEngine* e, const QByteArray& v) { return QScriptValue(QString(b64)); } +inline QScriptValue convertScriptValue(QScriptEngine* e, const EntityItemID& v) { return QScriptValue(QUuid(v).toString()); } + + #define COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(G,g,P,p) \ if (!skipDefaults || defaultEntityProperties.get##G().get##P() != get##P()) { \ QScriptValue groupProperties = properties.property(#g); \ @@ -143,6 +148,9 @@ inline int int_convertFromScriptValue(const QScriptValue& v, bool& isValid) { re inline bool bool_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toBool(); } inline QString QString_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toString().trimmed(); } inline QUuid QUuid_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toUuid(); } +inline EntityItemID EntityItemID_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toUuid(); } + + inline QDateTime QDateTime_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index abb8241d8f..d929bdbef5 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -124,21 +124,28 @@ enum EntityPropertyList { PROP_FACE_CAMERA, PROP_SCRIPT_TIMESTAMP, - + PROP_ACTION_DATA, - + PROP_X_TEXTURE_URL, // used by PolyVox PROP_Y_TEXTURE_URL, // used by PolyVox PROP_Z_TEXTURE_URL, // used by PolyVox - + // Used by PolyLine entity PROP_NORMALS, PROP_STROKE_WIDTHS, - + // used by particles PROP_VELOCITY_SPREAD, PROP_ACCELERATION_SPREAD, + PROP_X_N_NEIGHBOR_ID, // used by PolyVox + PROP_Y_N_NEIGHBOR_ID, // used by PolyVox + PROP_Z_N_NEIGHBOR_ID, // used by PolyVox + PROP_X_P_NEIGHBOR_ID, // used by PolyVox + PROP_Y_P_NEIGHBOR_ID, // used by PolyVox + PROP_Z_P_NEIGHBOR_ID, // used by PolyVox + //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties to end of list just ABOVE this line PROP_AFTER_LAST_ITEM, diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index fee2055bd0..b337c05776 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -493,6 +493,13 @@ bool EntityScriptingInterface::setAllVoxels(QUuid entityID, int value) { }); } +bool EntityScriptingInterface::setVoxelsInCuboid(QUuid entityID, const glm::vec3& lowPosition, + const glm::vec3& cuboidSize, int value) { + return setVoxels(entityID, [lowPosition, cuboidSize, value](PolyVoxEntityItem& polyVoxEntity) { + return polyVoxEntity.setCuboid(lowPosition, cuboidSize, value); + }); +} + bool EntityScriptingInterface::setAllPoints(QUuid entityID, const QVector& points) { EntityItemPointer entity = static_cast(_entityTree->findEntityByEntityItemID(entityID)); if (!entity) { diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index a51ebfb61c..ff693e4585 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -122,6 +122,8 @@ public slots: Q_INVOKABLE bool setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value); Q_INVOKABLE bool setVoxel(QUuid entityID, const glm::vec3& position, int value); Q_INVOKABLE bool setAllVoxels(QUuid entityID, int value); + Q_INVOKABLE bool setVoxelsInCuboid(QUuid entityID, const glm::vec3& lowPosition, + const glm::vec3& cuboidSize, int value); Q_INVOKABLE bool setAllPoints(QUuid entityID, const QVector& points); Q_INVOKABLE bool appendPoint(QUuid entityID, const glm::vec3& point); diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index e765afd430..6c53dbfa16 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -12,6 +12,7 @@ #include #include +#include #include @@ -56,13 +57,14 @@ PolyVoxEntityItem::PolyVoxEntityItem(const EntityItemID& entityItemID, const Ent _voxelSurfaceStyle(PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE), _xTextureURL(PolyVoxEntityItem::DEFAULT_X_TEXTURE_URL), _yTextureURL(PolyVoxEntityItem::DEFAULT_Y_TEXTURE_URL), - _zTextureURL(PolyVoxEntityItem::DEFAULT_Z_TEXTURE_URL) -{ + _zTextureURL(PolyVoxEntityItem::DEFAULT_Z_TEXTURE_URL) { _type = EntityTypes::PolyVox; setProperties(properties); } void PolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) { + QWriteLocker(&this->_voxelDataLock); + assert((int)_voxelVolumeSize.x == _voxelVolumeSize.x); assert((int)_voxelVolumeSize.y == _voxelVolumeSize.y); assert((int)_voxelVolumeSize.z == _voxelVolumeSize.z); @@ -96,6 +98,12 @@ void PolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) { } } +const glm::vec3& PolyVoxEntityItem::getVoxelVolumeSize() const { + QWriteLocker locker(&this->_voxelDataLock); + return _voxelVolumeSize; +} + + EntityItemProperties PolyVoxEntityItem::getProperties() const { EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class COPY_ENTITY_PROPERTY_TO_PROPERTIES(voxelVolumeSize, getVoxelVolumeSize); @@ -104,6 +112,12 @@ EntityItemProperties PolyVoxEntityItem::getProperties() const { COPY_ENTITY_PROPERTY_TO_PROPERTIES(xTextureURL, getXTextureURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(yTextureURL, getYTextureURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(zTextureURL, getZTextureURL); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(xNNeighborID, getXNNeighborID); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(yNNeighborID, getYNNeighborID); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(zNNeighborID, getZNNeighborID); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(xPNeighborID, getXPNeighborID); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(yPNeighborID, getYPNeighborID); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(zPNeighborID, getZPNeighborID); return properties; } @@ -116,6 +130,12 @@ bool PolyVoxEntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(xTextureURL, setXTextureURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(yTextureURL, setYTextureURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(zTextureURL, setZTextureURL); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(xNNeighborID, setXNNeighborID); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(yNNeighborID, setYNNeighborID); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(zNNeighborID, setZNNeighborID); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(xPNeighborID, setXPNeighborID); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(yPNeighborID, setYPNeighborID); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(zPNeighborID, setZPNeighborID); if (somethingChanged) { bool wantDebug = false; @@ -143,6 +163,12 @@ int PolyVoxEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* dat READ_ENTITY_PROPERTY(PROP_X_TEXTURE_URL, QString, setXTextureURL); READ_ENTITY_PROPERTY(PROP_Y_TEXTURE_URL, QString, setYTextureURL); READ_ENTITY_PROPERTY(PROP_Z_TEXTURE_URL, QString, setZTextureURL); + READ_ENTITY_PROPERTY(PROP_X_N_NEIGHBOR_ID, EntityItemID, setXNNeighborID); + READ_ENTITY_PROPERTY(PROP_Y_N_NEIGHBOR_ID, EntityItemID, setYNNeighborID); + READ_ENTITY_PROPERTY(PROP_Z_N_NEIGHBOR_ID, EntityItemID, setZNNeighborID); + READ_ENTITY_PROPERTY(PROP_X_P_NEIGHBOR_ID, EntityItemID, setXPNeighborID); + READ_ENTITY_PROPERTY(PROP_Y_P_NEIGHBOR_ID, EntityItemID, setYPNeighborID); + READ_ENTITY_PROPERTY(PROP_Z_P_NEIGHBOR_ID, EntityItemID, setZPNeighborID); return bytesRead; } @@ -157,16 +183,22 @@ EntityPropertyFlags PolyVoxEntityItem::getEntityProperties(EncodeBitstreamParams requestedProperties += PROP_X_TEXTURE_URL; requestedProperties += PROP_Y_TEXTURE_URL; requestedProperties += PROP_Z_TEXTURE_URL; + requestedProperties += PROP_X_N_NEIGHBOR_ID; + requestedProperties += PROP_Y_N_NEIGHBOR_ID; + requestedProperties += PROP_Z_N_NEIGHBOR_ID; + requestedProperties += PROP_X_P_NEIGHBOR_ID; + requestedProperties += PROP_Y_P_NEIGHBOR_ID; + requestedProperties += PROP_Z_P_NEIGHBOR_ID; return requestedProperties; } -void PolyVoxEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, +void PolyVoxEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { + int& propertyCount, + OctreeElement::AppendState& appendState) const { bool successPropertyFits = true; APPEND_ENTITY_PROPERTY(PROP_VOXEL_VOLUME_SIZE, getVoxelVolumeSize()); @@ -175,7 +207,12 @@ void PolyVoxEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeB APPEND_ENTITY_PROPERTY(PROP_X_TEXTURE_URL, getXTextureURL()); APPEND_ENTITY_PROPERTY(PROP_Y_TEXTURE_URL, getYTextureURL()); APPEND_ENTITY_PROPERTY(PROP_Z_TEXTURE_URL, getZTextureURL()); - + APPEND_ENTITY_PROPERTY(PROP_X_N_NEIGHBOR_ID, getXNNeighborID()); + APPEND_ENTITY_PROPERTY(PROP_Y_N_NEIGHBOR_ID, getYNNeighborID()); + APPEND_ENTITY_PROPERTY(PROP_Z_N_NEIGHBOR_ID, getZNNeighborID()); + APPEND_ENTITY_PROPERTY(PROP_X_P_NEIGHBOR_ID, getXPNeighborID()); + APPEND_ENTITY_PROPERTY(PROP_Y_P_NEIGHBOR_ID, getYPNeighborID()); + APPEND_ENTITY_PROPERTY(PROP_Z_P_NEIGHBOR_ID, getZPNeighborID()); } void PolyVoxEntityItem::debugDump() const { @@ -187,15 +224,12 @@ void PolyVoxEntityItem::debugDump() const { } void PolyVoxEntityItem::setVoxelData(QByteArray voxelData) { - _voxelDataLock.lockForWrite(); + QWriteLocker(&this->_voxelDataLock); _voxelData = voxelData; _voxelDataDirty = true; - _voxelDataLock.unlock(); } const QByteArray PolyVoxEntityItem::getVoxelData() const { - _voxelDataLock.lockForRead(); - auto result = _voxelData; - _voxelDataLock.unlock(); - return result; + QReadLocker(&this->_voxelDataLock); + return _voxelData; } diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index c84dc9f4c1..20a0646c9b 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -50,7 +50,7 @@ class PolyVoxEntityItem : public EntityItem { virtual void debugDump() const; virtual void setVoxelVolumeSize(glm::vec3 voxelVolumeSize); - virtual const glm::vec3& getVoxelVolumeSize() const { return _voxelVolumeSize; } + virtual const glm::vec3& getVoxelVolumeSize() const; virtual void setVoxelData(QByteArray voxelData); virtual const QByteArray getVoxelData() const; @@ -85,6 +85,7 @@ class PolyVoxEntityItem : public EntityItem { // coords are in world-space virtual bool setSphere(glm::vec3 center, float radius, uint8_t toValue) { return false; } virtual bool setAll(uint8_t toValue) { return false; } + virtual bool setCuboid(const glm::vec3& lowPosition, const glm::vec3& cuboidSize, int value) { return false; } virtual uint8_t getVoxel(int x, int y, int z) { return 0; } virtual bool setVoxel(int x, int y, int z, uint8_t toValue) { return false; } @@ -103,6 +104,28 @@ class PolyVoxEntityItem : public EntityItem { virtual void setZTextureURL(QString zTextureURL) { _zTextureURL = zTextureURL; } virtual const QString& getZTextureURL() const { return _zTextureURL; } + virtual void setXNNeighborID(const EntityItemID& xNNeighborID) { _xNNeighborID = xNNeighborID; } + void setXNNeighborID(const QString& xNNeighborID) { setXNNeighborID(QUuid(xNNeighborID)); } + virtual const EntityItemID& getXNNeighborID() const { return _xNNeighborID; } + virtual void setYNNeighborID(const EntityItemID& yNNeighborID) { _yNNeighborID = yNNeighborID; } + void setYNNeighborID(const QString& yNNeighborID) { setYNNeighborID(QUuid(yNNeighborID)); } + virtual const EntityItemID& getYNNeighborID() const { return _yNNeighborID; } + virtual void setZNNeighborID(const EntityItemID& zNNeighborID) { _zNNeighborID = zNNeighborID; } + void setZNNeighborID(const QString& zNNeighborID) { setZNNeighborID(QUuid(zNNeighborID)); } + virtual const EntityItemID& getZNNeighborID() const { return _zNNeighborID; } + + virtual void setXPNeighborID(const EntityItemID& xPNeighborID) { _xPNeighborID = xPNeighborID; } + void setXPNeighborID(const QString& xPNeighborID) { setXPNeighborID(QUuid(xPNeighborID)); } + virtual const EntityItemID& getXPNeighborID() const { return _xPNeighborID; } + virtual void setYPNeighborID(const EntityItemID& yPNeighborID) { _yPNeighborID = yPNeighborID; } + void setYPNeighborID(const QString& yPNeighborID) { setYPNeighborID(QUuid(yPNeighborID)); } + virtual const EntityItemID& getYPNeighborID() const { return _yPNeighborID; } + virtual void setZPNeighborID(const EntityItemID& zPNeighborID) { _zPNeighborID = zPNeighborID; } + void setZPNeighborID(const QString& zPNeighborID) { setZPNeighborID(QUuid(zPNeighborID)); } + virtual const EntityItemID& getZPNeighborID() const { return _zPNeighborID; } + + virtual void rebakeMesh() {}; + protected: glm::vec3 _voxelVolumeSize; // this is always 3 bytes @@ -116,6 +139,14 @@ class PolyVoxEntityItem : public EntityItem { QString _yTextureURL; QString _zTextureURL; + // for non-edged surface styles, these are used to compute the high-axis edges + EntityItemID _xNNeighborID{UNKNOWN_ENTITY_ID}; + EntityItemID _yNNeighborID{UNKNOWN_ENTITY_ID}; + EntityItemID _zNNeighborID{UNKNOWN_ENTITY_ID}; + + EntityItemID _xPNeighborID{UNKNOWN_ENTITY_ID}; + EntityItemID _yPNeighborID{UNKNOWN_ENTITY_ID}; + EntityItemID _zPNeighborID{UNKNOWN_ENTITY_ID}; }; #endif // hifi_PolyVoxEntityItem_h diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 8ce9f8c72e..35390a8e44 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2562,6 +2562,7 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping int maxJointIndex = firstFBXCluster.jointIndex; glm::mat4 inverseModelTransform = glm::inverse(modelTransform); if (clusterIDs.size() > 1) { + // this is a multi-mesh joint extracted.mesh.clusterIndices.resize(extracted.mesh.vertices.size()); extracted.mesh.clusterWeights.resize(extracted.mesh.vertices.size()); float maxWeight = 0.0f; @@ -2645,6 +2646,7 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping } } } else { + // this is a single-mesh joint int jointIndex = maxJointIndex; FBXJoint& joint = geometry.joints[jointIndex]; JointShapeInfo& jointShapeInfo = jointShapeInfos[jointIndex]; @@ -2665,6 +2667,7 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping } float radiusScale = extractUniformScale(joint.transform * firstFBXCluster.inverseBindMatrix); + // compute average vertex glm::vec3 averageVertex(0.0f); foreach (const glm::vec3& vertex, extracted.mesh.vertices) { float proj = glm::dot(boneDirection, boneEnd - vertex); @@ -2674,29 +2677,26 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping ++jointShapeInfo.numVertexWeights; averageVertex += vertex; } + + // compute joint's radius int numVertices = extracted.mesh.vertices.size(); jointShapeInfo.numVertices = numVertices; if (numVertices > 0) { + // compute average radius averageVertex /= (float)jointShapeInfo.numVertices; float averageRadius = 0.0f; foreach (const glm::vec3& vertex, extracted.mesh.vertices) { averageRadius += glm::distance(vertex, averageVertex); } - jointShapeInfo.averageRadius = averageRadius * radiusScale / (float)jointShapeInfo.numVertices; + averageRadius *= radiusScale / (float)jointShapeInfo.numVertices; + + // final radius is minimum of average and weighted + float weightedRadius = jointShapeInfo.sumWeightedRadii / jointShapeInfo.sumVertexWeights; + jointShapeInfo.averageRadius = glm::min(weightedRadius, averageRadius); } - // BUG: the boneBegin and/or boneEnd are incorrect for meshes that are "connected - // under the bone" without weights. Unfortunately we haven't been able to find it yet. - // Although the the mesh vertices are correct in the model-frame, the joint's transform - // in the same frame is just BAD. - // - // HACK WORKAROUND: prevent these shapes from contributing to the collision capsule by setting - // some key members of jointShapeInfo to zero: - jointShapeInfo.numVertices = 0; + // clear sumVertexWeights (this flags it as a single-mesh joint for later) jointShapeInfo.sumVertexWeights = 0.0f; - jointShapeInfo.numVertexWeights = 0; - jointShapeInfo.boneBegin = glm::vec3(0.0f); - jointShapeInfo.averageRadius = 0.0f; } extracted.mesh.isEye = (maxJointIndex == geometry.leftEyeJointIndex || maxJointIndex == geometry.rightEyeJointIndex); @@ -2732,22 +2732,12 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping } if (jointShapeInfo.sumVertexWeights > 0.0f) { + // mutiple meshes contributed to the bone radius and now that all + // contributing meshes are done we can finally compute the boneRadius joint.boneRadius = jointShapeInfo.sumWeightedRadii / jointShapeInfo.sumVertexWeights; - } - - // the joint is "capsule-like" if it had ANY mesh vertices successfully projected onto the bone - // AND its boneRadius is not too close to zero - bool collideLikeCapsule = jointShapeInfo.numVertexWeights > 0 - && glm::length(jointShapeInfo.boneBegin) > EPSILON; - - if (!collideLikeCapsule) { - // this joint's mesh did not successfully project onto the bone axis - // so it isn't "capsule-like" and we need to estimate its radius a different way: - // the average radius to the average point. - if (jointShapeInfo.numVertexWeights == 0 - && jointShapeInfo.numVertices > 0) { - joint.boneRadius = jointShapeInfo.averageRadius; - } + } else { + // single-mesh joint + joint.boneRadius = jointShapeInfo.averageRadius; } } geometry.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString()); diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index ac72f1bd68..e9eca409e6 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -67,7 +67,7 @@ PacketVersion versionForPacketType(PacketType::Value packetType) { case EntityAdd: case EntityEdit: case EntityData: - return VERSION_ENTITIES_PARTICLE_MODIFICATIONS; + return VERSION_ENTITIES_POLYVOX_NEIGHBORS; case AvatarData: return 13; default: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index fa6178b627..d22ee59cad 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -144,5 +144,6 @@ const PacketVersion VERSION_POLYVOX_TEXTURES = 36; const PacketVersion VERSION_ENTITIES_POLYLINE = 37; const PacketVersion VERSION_OCTREE_CENTERED_ORIGIN = 38; const PacketVersion VERSION_ENTITIES_PARTICLE_MODIFICATIONS = 39; +const PacketVersion VERSION_ENTITIES_POLYVOX_NEIGHBORS = 40; -#endif // hifi_PacketHeaders_h \ No newline at end of file +#endif // hifi_PacketHeaders_h