From feaf5678bbaeb499ed4ae21bf9889c4df2fbd1f4 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 6 Nov 2014 15:00:39 -0800 Subject: [PATCH] Add grid tools to entity edit tools --- examples/libraries/entitySelectionTool.js | 73 +++-- examples/libraries/gridTool.js | 266 +++++++++++++++++++ examples/newEditEntities.js | 27 +- gridControls.html | 206 ++++++++++++++ libraries/script-engine/src/ScriptEngine.cpp | 5 + libraries/script-engine/src/ScriptEngine.h | 1 + 6 files changed, 543 insertions(+), 35 deletions(-) create mode 100644 examples/libraries/gridTool.js create mode 100644 gridControls.html diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 29bf1bdd79..980689d625 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -394,8 +394,7 @@ SelectionDisplay = (function () { var baseOverlayAngles = { x: 0, y: 0, z: 0 }; var baseOverlayRotation = Quat.fromVec3Degrees(baseOverlayAngles); var baseOfEntityProjectionOverlay = Overlays.addOverlay("rectangle3d", { - position: { x:0, y: 0, z: 0}, - size: 1, + position: { x: 1, y: 0, z: 0}, color: { red: 51, green: 152, blue: 203 }, alpha: 0.5, solid: true, @@ -570,6 +569,7 @@ SelectionDisplay = (function () { xRailOverlay, yRailOverlay, zRailOverlay, + baseOfEntityProjectionOverlay, ].concat(stretchHandles); overlayNames[highlightBox] = "highlightBox"; @@ -878,20 +878,24 @@ SelectionDisplay = (function () { translateHandlesVisible = false; } - var rotation = SelectionManager.worldRotation; - var dimensions = SelectionManager.worldDimensions; - var position = SelectionManager.worldPosition; + var rotation = selectionManager.worldRotation; + var dimensions = selectionManager.worldDimensions; + var position = selectionManager.worldPosition; Overlays.editOverlay(baseOfEntityProjectionOverlay, { - visible: true, - solid:true, - lineWidth: 2.0, - position: { x: position.x, - y: 0, - z: position.z }, - - dimensions: { x: dimensions.x, y: 0, z: dimensions.z }, + visible: mode != "ROTATE_YAW" && mode != "ROTATE_PITCH" && mode != "ROTATE_ROLL", + solid: true, + // lineWidth: 2.0, + position: { + x: position.x, + y: grid.getOrigin().y, + z: position.z + }, + dimensions: { + x: dimensions.x, + y: dimensions.z + }, rotation: rotation, }); @@ -1098,6 +1102,7 @@ SelectionDisplay = (function () { var initialXZPick = null; var isConstrained = false; + var constrainMajorOnly = false; var startPosition = null; var duplicatedEntityIDs = null; var translateXZTool = { @@ -1143,34 +1148,46 @@ SelectionDisplay = (function () { // If shifted, constrain to one axis if (event.isShifted) { - if (Math.abs(vector.x) > Math.abs(vector.z)) { - vector.z = 0; - } else { - vector.x = 0; - } + // if (Math.abs(vector.x) > Math.abs(vector.z)) { + // vector.z = 0; + // } else { + // vector.x = 0; + // } if (!isConstrained) { - Overlays.editOverlay(xRailOverlay, { visible: true }); - var xStart = Vec3.sum(startPosition, { x: -10000, y: 0, z: 0 }); - var xEnd = Vec3.sum(startPosition, { x: 10000, y: 0, z: 0 }); - var zStart = Vec3.sum(startPosition, { x: 0, y: 0, z: -10000 }); - var zEnd = Vec3.sum(startPosition, { x: 0, y: 0, z: 10000 }); - Overlays.editOverlay(xRailOverlay, { start: xStart, end: xEnd, visible: true }); - Overlays.editOverlay(zRailOverlay, { start: zStart, end: zEnd, visible: true }); + // Overlays.editOverlay(xRailOverlay, { visible: true }); + // var xStart = Vec3.sum(startPosition, { x: -10000, y: 0, z: 0 }); + // var xEnd = Vec3.sum(startPosition, { x: 10000, y: 0, z: 0 }); + // var zStart = Vec3.sum(startPosition, { x: 0, y: 0, z: -10000 }); + // var zEnd = Vec3.sum(startPosition, { x: 0, y: 0, z: 10000 }); + // Overlays.editOverlay(xRailOverlay, { start: xStart, end: xEnd, visible: true }); + // Overlays.editOverlay(zRailOverlay, { start: zStart, end: zEnd, visible: true }); isConstrained = true; } + // constrainMajorOnly = event.isControl; + // vector = Vec3.subtract( + // grid.snapToGrid(Vec3.sum(startPosition, vector), constrainMajorOnly), + // startPosition); } else { if (isConstrained) { - Overlays.editOverlay(xRailOverlay, { visible: false }); - Overlays.editOverlay(zRailOverlay, { visible: false }); + // Overlays.editOverlay(xRailOverlay, { visible: false }); + // Overlays.editOverlay(zRailOverlay, { visible: false }); + isConstrained = false; } } + constrainMajorOnly = event.isControl; + var cornerPosition = Vec3.sum(startPosition, Vec3.multiply(-0.5, selectionManager.worldDimensions)); + vector = Vec3.subtract( + grid.snapToGrid(Vec3.sum(cornerPosition, vector), constrainMajorOnly), + cornerPosition); + var wantDebug = false; for (var i = 0; i < SelectionManager.selections.length; i++) { var properties = SelectionManager.savedProperties[SelectionManager.selections[i].id]; + var newPosition = Vec3.sum(properties.position, { x: vector.x, y: 0, z: vector.z }); Entities.editEntity(SelectionManager.selections[i], { - position: Vec3.sum(properties.position, vector), + position: newPosition, }); if (wantDebug) { diff --git a/examples/libraries/gridTool.js b/examples/libraries/gridTool.js new file mode 100644 index 0000000000..76c0581b2c --- /dev/null +++ b/examples/libraries/gridTool.js @@ -0,0 +1,266 @@ +Grid = function(opts) { + var that = {}; + + var color = { red: 100, green: 152, blue: 203 }; + var gridColor = { red: 100, green: 152, blue: 203 }; + var gridAlpha = 0.9; + var origin = { x: 0, y: 0, z: 0 }; + var majorGridEvery = 5; + var minorGridSpacing = 0.2; + var halfSize = 40; + var yOffset = 0.001; + + var worldSize = 16384; + + var minorGridWidth = 0.5; + var majorGridWidth = 1.5; + + var gridOverlays = []; + + var snapToGrid = true; + + var gridPlane = Overlays.addOverlay("rectangle3d", { + position: origin, + color: color, + size: halfSize * 2 * minorGridSpacing * 1.05, + alpha: 0.2, + solid: true, + visible: false, + ignoreRayIntersection: true, + }); + + that.getMinorIncrement = function() { return minorGridSpacing; }; + that.getMajorIncrement = function() { return minorGridSpacing * majorGridEvery; }; + + that.visible = false; + + that.getOrigin = function() { + return origin; + } + + that.getSnapToGrid = function() { return snapToGrid; }; + + that.setVisible = function(visible, noUpdate) { + that.visible = visible; + updateGrid(); + // for (var i = 0; i < gridOverlays.length; i++) { + // Overlays.editOverlay(gridOverlays[i], { visible: visible }); + // } + // Overlays.editOverlay(gridPlane, { visible: visible }); + + if (!noUpdate) { + that.emitUpdate(); + } + } + + that.snapToGrid = function(position, majorOnly) { + if (!snapToGrid) { + return position; + } + + var spacing = majorOnly ? (minorGridSpacing * majorGridEvery) : minorGridSpacing; + + position = Vec3.subtract(position, origin); + + position.x = Math.round(position.x / spacing) * spacing; + position.y = Math.round(position.y / spacing) * spacing; + position.z = Math.round(position.z / spacing) * spacing; + + return Vec3.sum(position, origin); + } + + that.setPosition = function(newPosition, noUpdate) { + origin = Vec3.subtract(newPosition, { x: 0, y: yOffset, z: 0 }); + updateGrid(); + + print("updated grid"); + if (!noUpdate) { + that.emitUpdate(); + } + }; + + that.emitUpdate = function() { + if (that.onUpdate) { + that.onUpdate({ + origin: origin, + minorGridSpacing: minorGridSpacing, + majorGridEvery: majorGridEvery, + gridSize: halfSize, + visible: that.visible, + snapToGrid: snapToGrid, + gridColor: gridColor, + }); + } + }; + + that.update = function(data) { + print("Got update"); + if (data.snapToGrid !== undefined) { + snapToGrid = data.snapToGrid; + } + + if (data.origin) { + var pos = data.origin; + pos.x = pos.x === undefined ? origin.x : pos.x; + pos.y = pos.y === undefined ? origin.y : pos.y; + pos.z = pos.z === undefined ? origin.z : pos.z; + that.setPosition(pos, true); + } + + if (data.minorGridSpacing) { + minorGridSpacing = data.minorGridSpacing; + } + + if (data.majorGridEvery) { + majorGridEvery = data.majorGridEvery; + } + + if (data.gridColor) { + gridColor = data.gridColor; + } + + if (data.gridSize) { + halfSize = data.gridSize; + } + + if (data.visible !== undefined) { + that.setVisible(data.visible, true); + } + + updateGrid(); + } + + function updateGrid() { + // Delete overlays + var gridLinesRequired = (halfSize * 2 + 1) * 2; + if (gridLinesRequired > gridOverlays.length) { + for (var i = gridOverlays.length; i < gridLinesRequired; i++) { + gridOverlays.push(Overlays.addOverlay("line3d", {})); + } + } else if (gridLinesRequired < gridOverlays.length) { + var numberToRemove = gridOverlays.length - gridLinesRequired; + var removed = gridOverlays.splice(gridOverlays.length - numberToRemove, numberToRemove); + for (var i = 0; i < removed.length; i++) { + Overlays.deleteOverlay(removed[i]); + } + } + + Overlays.editOverlay(gridPlane, { + position: origin, + size: halfSize * 2 * minorGridSpacing * 1.05, + }); + + var startX = { + x: origin.x - (halfSize * minorGridSpacing), + y: origin.y, + z: origin.z, + }; + var endX = { + x: origin.x + (halfSize * minorGridSpacing), + y: origin.y, + z: origin.z, + }; + var startZ = { + x: origin.x, + y: origin.y, + z: origin.z - (halfSize * minorGridSpacing) + }; + var endZ = { + x: origin.x, + y: origin.y, + z: origin.z + (halfSize * minorGridSpacing) + }; + + var overlayIdx = 0; + for (var i = -halfSize; i <= halfSize; i++) { + // Offset for X-axis aligned grid line + var offsetX = { x: 0, y: 0, z: i * minorGridSpacing }; + + // Offset for Z-axis aligned grid line + var offsetZ = { x: i * minorGridSpacing, y: 0, z: 0 }; + + var position = Vec3.sum(origin, offsetX); + var size = i % majorGridEvery == 0 ? majorGridWidth : minorGridWidth; + + var gridLineX = gridOverlays[overlayIdx++]; + var gridLineZ = gridOverlays[overlayIdx++]; + + Overlays.editOverlay(gridLineX, { + start: Vec3.sum(startX, offsetX), + end: Vec3.sum(endX, offsetX), + lineWidth: size, + color: gridColor, + alpha: gridAlpha, + solid: true, + visible: that.visible, + ignoreRayIntersection: true, + }); + Overlays.editOverlay(gridLineZ, { + start: Vec3.sum(startZ, offsetZ), + end: Vec3.sum(endZ, offsetZ), + lineWidth: size, + color: gridColor, + alpha: gridAlpha, + solid: true, + visible: that.visible, + ignoreRayIntersection: true, + }); + } + } + + function cleanup() { + Overlays.deleteOverlay(gridPlane); + for (var i = 0; i < gridOverlays.length; i++) { + Overlays.deleteOverlay(gridOverlays[i]); + } + } + + that.addListener = function(callback) { + that.onUpdate = callback; + } + + Script.scriptEnding.connect(cleanup); + updateGrid(); + + that.onUpdate = null; + + return that; +}; + +GridTool = function(opts) { + var that = {}; + + var horizontalGrid = opts.horizontalGrid; + var verticalGrid = opts.verticalGrid; + var listeners = []; + + // var webView = Window.createWebView('http://localhost:8000/gridControls.html', 200, 280); + var webView = new WebWindow('http://localhost:8000/gridControls.html', 200, 280); + + horizontalGrid.addListener(function(data) { + webView.eventBridge.emitScriptEvent(JSON.stringify(data)); + }); + + webView.eventBridge.webEventReceived.connect(function(data) { + print('got event: ' + data); + data = JSON.parse(data); + if (data.type == "init") { + horizontalGrid.emitUpdate(); + } else if (data.type == "update") { + horizontalGrid.update(data); + for (var i = 0; i < listeners.length; i++) { + listeners[i](data); + } + } + }); + + that.addListener = function(callback) { + listeners.push(callback); + } + + that.setVisible = function(visible) { + webView.setVisible(visible); + } + + return that; +}; diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js index ea1f69e15f..b8e7a6174e 100644 --- a/examples/newEditEntities.js +++ b/examples/newEditEntities.js @@ -35,6 +35,12 @@ var entityPropertyDialogBox = EntityPropertyDialogBox; Script.include("libraries/entityCameraTool.js"); var cameraManager = new CameraManager(); +Script.include("libraries/gridTool.js"); +var grid = Grid(); +gridTool = GridTool({ horizontalGrid: grid }); +gridTool.addListener(function(data) { +}); + selectionManager.setEventListener(selectionDisplay.updateHandles); var windowDimensions = Controller.getViewportDimensions(); @@ -258,6 +264,7 @@ var toolBar = (function () { if (activeButton === toolBar.clicked(clickedOverlay)) { isActive = !isActive; + gridTool.setVisible(isActive); if (!isActive) { selectionManager.clearSelections(); cameraManager.disable(); @@ -747,25 +754,32 @@ Controller.keyReleaseEvent.connect(function (event) { if (isActive) { cameraManager.enable(); } + } else if (event.text == 'g') { + if (isActive && selectionManager.hasSelection()) { + var newPosition = selectionManager.worldPosition; + newPosition = Vec3.subtract(newPosition, { x: 0, y: selectionManager.worldDimensions.y * 0.5, z: 0 }); + grid.setPosition(newPosition); + } } else if (isActive) { var delta = null; + var increment = event.isShifted ? grid.getMajorIncrement() : grid.getMinorIncrement(); if (event.text == 'UP') { if (event.isControl || event.isAlt) { - delta = { x: 0, y: 1, z: 0 }; + delta = { x: 0, y: increment, z: 0 }; } else { - delta = { x: 0, y: 0, z: -1 }; + delta = { x: 0, y: 0, z: -increment }; } } else if (event.text == 'DOWN') { if (event.isControl || event.isAlt) { - delta = { x: 0, y: -1, z: 0 }; + delta = { x: 0, y: -increment, z: 0 }; } else { - delta = { x: 0, y: 0, z: 1 }; + delta = { x: 0, y: 0, z: increment }; } } else if (event.text == 'LEFT') { - delta = { x: -1, y: 0, z: 0 }; + delta = { x: -increment, y: 0, z: 0 }; } else if (event.text == 'RIGHT') { - delta = { x: 1, y: 0, z: 0 }; + delta = { x: increment, y: 0, z: 0 }; } if (delta != null) { @@ -820,7 +834,6 @@ function applyEntityProperties(data) { var properties = data.createEntities[i].properties; var newEntityID = Entities.addEntity(properties); DELETED_ENTITY_MAP[entityID.id] = newEntityID; - print(newEntityID.isKnownID); if (data.selectCreated) { selectedEntityIDs.push(newEntityID); } diff --git a/gridControls.html b/gridControls.html new file mode 100644 index 0000000000..84d437a60d --- /dev/null +++ b/gridControls.html @@ -0,0 +1,206 @@ + + + + + + +
+ + +
+
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+
+ + diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index fb98124fc9..8f176de04f 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -322,6 +322,11 @@ QScriptValue ScriptEngine::registerGlobalObject(const QString& name, QObject* ob return QScriptValue::NullValue; } +void ScriptEngine::registerFunction(const QString& name, QScriptEngine::FunctionSignature fun, int numArguments) { + QScriptValue scriptFun = newFunction(fun, numArguments); + globalObject().setProperty(name, scriptFun); +} + void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter, QScriptEngine::FunctionSignature setter, QScriptValue object) { QScriptValue setterFunction = newFunction(setter, 1); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index d556475859..22617512a9 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -65,6 +65,7 @@ public: QScriptValue registerGlobalObject(const QString& name, QObject* object); /// registers a global object by name void registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter, QScriptEngine::FunctionSignature setter, QScriptValue object = QScriptValue::NullValue); + void registerFunction(const QString& name, QScriptEngine::FunctionSignature fun, int numArguments = -1); Q_INVOKABLE void setIsAvatar(bool isAvatar); bool isAvatar() const { return _isAvatar; }