diff --git a/examples/html/gridControls.html b/examples/html/gridControls.html new file mode 100644 index 0000000000..e7bf1cdf8c --- /dev/null +++ b/examples/html/gridControls.html @@ -0,0 +1,193 @@ + + + + + + +
+ + +
+
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+
+ + diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 29bf1bdd79..22f75cb187 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -394,12 +394,12 @@ 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, visible: false, + width: 300, height: 200, rotation: baseOverlayRotation, ignoreRayIntersection: true, // always ignore this }); @@ -570,6 +570,7 @@ SelectionDisplay = (function () { xRailOverlay, yRailOverlay, zRailOverlay, + baseOfEntityProjectionOverlay, ].concat(stretchHandles); overlayNames[highlightBox] = "highlightBox"; @@ -878,20 +879,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 +1103,7 @@ SelectionDisplay = (function () { var initialXZPick = null; var isConstrained = false; + var constrainMajorOnly = false; var startPosition = null; var duplicatedEntityIDs = null; var translateXZTool = { @@ -1162,15 +1168,23 @@ SelectionDisplay = (function () { if (isConstrained) { 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..34e25d6733 --- /dev/null +++ b/examples/libraries/gridTool.js @@ -0,0 +1,189 @@ +Grid = function(opts) { + var that = {}; + + var color = { red: 100, green: 152, blue: 203 }; + var gridColor = { red: 100, green: 152, blue: 203 }; + var gridAlpha = 1.0; + 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 snapToGrid = true; + + var gridOverlay = Overlays.addOverlay("grid", { + position: { x: 0 , y: 0, z: 0 }, + visible: true, + color: { red: 0, green: 0, blue: 128 }, + alpha: 1.0, + rotation: Quat.fromPitchYawRollDegrees(90, 0, 0), + minorGridWidth: 0.1, + majorGridEvery: 2, + }); + + 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(); + + 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 }); + origin.x = 0; + origin.z = 0; + updateGrid(); + + 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) { + 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() { + Overlays.editOverlay(gridOverlay, { + position: { x: origin.y, y: origin.y, z: -origin.y }, + visible: that.visible, + minorGridWidth: minorGridSpacing, + majorGridEvery: majorGridEvery, + color: gridColor, + alpha: gridAlpha, + }); + } + + function cleanup() { + Overlays.deleteOverlay(gridOverlay); + } + + 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 url = Script.resolvePath('html/gridControls.html'); + var webView = new WebWindow(url, 200, 280); + + horizontalGrid.addListener(function(data) { + webView.eventBridge.emitScriptEvent(JSON.stringify(data)); + }); + + webView.eventBridge.webEventReceived.connect(function(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 57f3f29670..68e0d0c146 100644 --- a/examples/newEditEntities.js +++ b/examples/newEditEntities.js @@ -35,6 +35,10 @@ var entityPropertyDialogBox = EntityPropertyDialogBox; Script.include("libraries/entityCameraTool.js"); var cameraManager = new CameraManager(); +Script.include("libraries/gridTool.js"); +var grid = Grid(); +gridTool = GridTool({ horizontalGrid: grid }); + selectionManager.setEventListener(selectionDisplay.updateHandles); var windowDimensions = Controller.getViewportDimensions(); @@ -51,11 +55,13 @@ var wantEntityGlow = false; var SPAWN_DISTANCE = 1; var DEFAULT_DIMENSION = 0.20; +var MENU_GRID_TOOL_ENABLED = 'Grid Tool'; var MENU_INSPECT_TOOL_ENABLED = "Inspect Tool"; var MENU_EASE_ON_FOCUS = "Ease Orientation on Focus"; var SETTING_INSPECT_TOOL_ENABLED = "inspectToolEnabled"; var SETTING_EASE_ON_FOCUS = "cameraEaseOnFocus"; +var SETTING_GRID_TOOL_ENABLED = 'GridToolEnabled'; var modelURLs = [ HIFI_PUBLIC_BUCKET + "models/entities/2-Terrain:%20Alder.fbx", @@ -262,10 +268,12 @@ var toolBar = (function () { if (activeButton === toolBar.clicked(clickedOverlay)) { isActive = !isActive; if (!isActive) { + gridTool.setVisible(false); selectionManager.clearSelections(); cameraManager.disable(); } else { cameraManager.enable(); + gridTool.setVisible(Menu.isOptionChecked(MENU_GRID_TOOL_ENABLED)); } return true; } @@ -597,7 +605,9 @@ function setupModelMenus() { Menu.addMenuItem({ menuName: "File", menuItemName: "Import Models", shortcutKey: "CTRL+META+I", afterItem: "Export Models" }); Menu.addMenuItem({ menuName: "Developer", menuItemName: "Debug Ryans Rotation Problems", isCheckable: true }); - Menu.addMenuItem({ menuName: "View", menuItemName: MENU_INSPECT_TOOL_ENABLED, afterItem: "Edit Entities Help...", + Menu.addMenuItem({ menuName: "View", menuItemName: MENU_GRID_TOOL_ENABLED, afterItem: "Edit Entities Help...", isCheckable: true, + isChecked: Settings.getValue(SETTING_GRID_TOOL_ENABLED) == 'true'}); + Menu.addMenuItem({ menuName: "View", menuItemName: MENU_INSPECT_TOOL_ENABLED, afterItem: MENU_GRID_TOOL_ENABLED, isCheckable: true, isChecked: Settings.getValue(SETTING_INSPECT_TOOL_ENABLED) == "true" }); Menu.addMenuItem({ menuName: "View", menuItemName: MENU_EASE_ON_FOCUS, afterItem: MENU_INSPECT_TOOL_ENABLED, isCheckable: true, isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true" }); @@ -623,6 +633,8 @@ function cleanupModelMenus() { Menu.removeMenuItem("File", "Import Models"); Menu.removeMenuItem("Developer", "Debug Ryans Rotation Problems"); + Settings.setValue(SETTING_GRID_TOOL_ENABLED, Menu.isOptionChecked(MENU_GRID_TOOL_ENABLED)); + Menu.removeMenuItem("View", MENU_GRID_TOOL_ENABLED); Menu.removeMenuItem("View", MENU_INSPECT_TOOL_ENABLED); Menu.removeMenuItem("View", MENU_EASE_ON_FOCUS); } @@ -734,6 +746,10 @@ function handeMenuEvent(menuItem) { } } else if (menuItem == "Import Models") { modelImporter.doImport(); + } else if (menuItem == MENU_GRID_TOOL_ENABLED) { + if (isActive) { + gridTool.setVisible(Menu.isOptionChecked(MENU_GRID_TOOL_ENABLED)); + } } tooltip.show(false); } @@ -759,25 +775,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) { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d54a638239..b1c969b66f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -92,6 +92,7 @@ #include "scripting/MenuScriptingInterface.h" #include "scripting/SettingsScriptingInterface.h" #include "scripting/WindowScriptingInterface.h" +#include "scripting/WebWindowClass.h" #include "ui/DataWebDialog.h" #include "ui/InfoView.h" @@ -3897,6 +3898,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri // register `location` on the global object. scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter, LocationScriptingInterface::locationSetter); + + scriptEngine->registerFunction("WebWindow", WebWindowClass::constructor, 1); scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance()); diff --git a/interface/src/scripting/WebWindowClass.cpp b/interface/src/scripting/WebWindowClass.cpp new file mode 100644 index 0000000000..d280d8eecf --- /dev/null +++ b/interface/src/scripting/WebWindowClass.cpp @@ -0,0 +1,68 @@ +// +// WebWindowClass.cpp +// interface/src/scripting +// +// Created by Ryan Huffman on 11/06/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include +#include + +#include "WindowScriptingInterface.h" +#include "WebWindowClass.h" + +ScriptEventBridge::ScriptEventBridge(QObject* parent) : QObject(parent) { +} + +void ScriptEventBridge::emitWebEvent(const QString& data) { + emit webEventReceived(data); +} + +void ScriptEventBridge::emitScriptEvent(const QString& data) { + emit scriptEventReceived(data); +} + +WebWindowClass::WebWindowClass(const QString& url, int width, int height) + : QObject(NULL), + _window(new QWidget(NULL, Qt::Tool)), + _eventBridge(new ScriptEventBridge(this)) { + + QWebView* webView = new QWebView(_window); + webView->page()->mainFrame()->addToJavaScriptWindowObject("EventBridge", _eventBridge); + webView->setUrl(url); + QVBoxLayout* layout = new QVBoxLayout(_window); + _window->setLayout(layout); + layout->addWidget(webView); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + _window->setGeometry(0, 0, width, height); + + connect(this, &WebWindowClass::destroyed, _window, &QWidget::deleteLater); +} + +WebWindowClass::~WebWindowClass() { +} + +void WebWindowClass::setVisible(bool visible) { + QMetaObject::invokeMethod(_window, "setVisible", Qt::BlockingQueuedConnection, Q_ARG(bool, visible)); +} + +QScriptValue WebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) { + WebWindowClass* retVal; + QString file = context->argument(0).toString(); + QMetaObject::invokeMethod(WindowScriptingInterface::getInstance(), "doCreateWebWindow", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(WebWindowClass*, retVal), + Q_ARG(const QString&, file), + Q_ARG(int, context->argument(1).toInteger()), + Q_ARG(int, context->argument(2).toInteger())); + + connect(engine, &QScriptEngine::destroyed, retVal, &WebWindowClass::deleteLater); + + return engine->newQObject(retVal); +} diff --git a/interface/src/scripting/WebWindowClass.h b/interface/src/scripting/WebWindowClass.h new file mode 100644 index 0000000000..7b77299774 --- /dev/null +++ b/interface/src/scripting/WebWindowClass.h @@ -0,0 +1,51 @@ +// +// WebWindowClass.h +// interface/src/scripting +// +// Created by Ryan Huffman on 11/06/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_WebWindowClass_h +#define hifi_WebWindowClass_h + +#include +#include + +class ScriptEventBridge : public QObject { + Q_OBJECT +public: + ScriptEventBridge(QObject* parent = NULL); + +public slots: + void emitWebEvent(const QString& data); + void emitScriptEvent(const QString& data); + +signals: + void webEventReceived(const QString& data); + void scriptEventReceived(const QString& data); + +}; + +class WebWindowClass : public QObject { + Q_OBJECT + Q_PROPERTY(QObject* eventBridge READ getEventBridge) +public: + WebWindowClass(const QString& url, int width, int height); + ~WebWindowClass(); + + static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); + +public slots: + void setVisible(bool visible); + ScriptEventBridge* getEventBridge() const { return _eventBridge; } + +private: + QWidget* _window; + ScriptEventBridge* _eventBridge; +}; + +#endif diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index ed6f0cf600..8c2066f253 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -34,6 +34,10 @@ WindowScriptingInterface::WindowScriptingInterface() : { } +WebWindowClass* WindowScriptingInterface::doCreateWebWindow(const QString& url, int width, int height) { + return new WebWindowClass(url, width, height); +} + QScriptValue WindowScriptingInterface::hasFocus() { return Application::getInstance()->getGLWidget()->hasFocus(); } diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 24c21765b5..5529d31efd 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -15,6 +15,11 @@ #include #include #include +#include +#include +#include + +#include "WebWindowClass.h" class WindowScriptingInterface : public QObject { Q_OBJECT @@ -72,6 +77,8 @@ private slots: void nonBlockingFormAccepted() { _nonBlockingFormActive = false; _formResult = QDialog::Accepted; emit nonBlockingFormClosed(); } void nonBlockingFormRejected() { _nonBlockingFormActive = false; _formResult = QDialog::Rejected; emit nonBlockingFormClosed(); } + + WebWindowClass* doCreateWebWindow(const QString& url, int width, int height); private: WindowScriptingInterface(); diff --git a/interface/src/ui/overlays/Grid3DOverlay.cpp b/interface/src/ui/overlays/Grid3DOverlay.cpp new file mode 100644 index 0000000000..c628199fe3 --- /dev/null +++ b/interface/src/ui/overlays/Grid3DOverlay.cpp @@ -0,0 +1,118 @@ +// +// Grid3DOverlay.cpp +// interface/src/ui/overlays +// +// Created by Ryan Huffman on 11/06/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "Grid3DOverlay.h" + +#include "Application.h" + +ProgramObject Grid3DOverlay::_gridProgram; + +Grid3DOverlay::Grid3DOverlay() : Base3DOverlay(), + _minorGridWidth(1.0), + _majorGridEvery(5) { +} + +Grid3DOverlay::~Grid3DOverlay() { +} + +void Grid3DOverlay::render(RenderArgs* args) { + if (!_visible) { + return; // do nothing if we're not visible + } + + if (!_gridProgram.isLinked()) { + if (!_gridProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + "shaders/grid.frag")) { + qDebug() << "Failed to compile: " + _gridProgram.log(); + return; + } + if (!_gridProgram.link()) { + qDebug() << "Failed to link: " + _gridProgram.log(); + return; + } + } + + // Render code largely taken from MetavoxelEditor::render() + glDisable(GL_LIGHTING); + + glDepthMask(GL_FALSE); + + glPushMatrix(); + + glm::quat rotation = getRotation(); + + glm::vec3 axis = glm::axis(rotation); + + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + + glLineWidth(1.5f); + + // center the grid around the camera position on the plane + glm::vec3 rotated = glm::inverse(rotation) * Application::getInstance()->getCamera()->getPosition(); + float spacing = _minorGridWidth; + + float alpha = getAlpha(); + xColor color = getColor(); + glm::vec3 position = getPosition(); + + const int GRID_DIVISIONS = 300; + const float MAX_COLOR = 255.0f; + float scale = GRID_DIVISIONS * spacing; + + glColor4f(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); + + _gridProgram.bind(); + + // Minor grid + glPushMatrix(); + { + glTranslatef(_minorGridWidth * (floorf(rotated.x / spacing) - GRID_DIVISIONS / 2), + spacing * (floorf(rotated.y / spacing) - GRID_DIVISIONS / 2), position.z); + + glScalef(scale, scale, scale); + + Application::getInstance()->getGeometryCache()->renderGrid(GRID_DIVISIONS, GRID_DIVISIONS); + } + glPopMatrix(); + + // Major grid + glPushMatrix(); + { + glLineWidth(4.0f); + spacing *= _majorGridEvery; + glTranslatef(spacing * (floorf(rotated.x / spacing) - GRID_DIVISIONS / 2), + spacing * (floorf(rotated.y / spacing) - GRID_DIVISIONS / 2), position.z); + + scale *= _majorGridEvery; + glScalef(scale, scale, scale); + + Application::getInstance()->getGeometryCache()->renderGrid(GRID_DIVISIONS, GRID_DIVISIONS); + } + glPopMatrix(); + + _gridProgram.release(); + + glPopMatrix(); + + glEnable(GL_LIGHTING); + glDepthMask(GL_TRUE); +} + +void Grid3DOverlay::setProperties(const QScriptValue& properties) { + Base3DOverlay::setProperties(properties); + + if (properties.property("minorGridWidth").isValid()) { + _minorGridWidth = properties.property("minorGridWidth").toVariant().toFloat(); + } + + if (properties.property("majorGridEvery").isValid()) { + _majorGridEvery = properties.property("majorGridEvery").toVariant().toInt(); + } +} diff --git a/interface/src/ui/overlays/Grid3DOverlay.h b/interface/src/ui/overlays/Grid3DOverlay.h new file mode 100644 index 0000000000..b1675f15d7 --- /dev/null +++ b/interface/src/ui/overlays/Grid3DOverlay.h @@ -0,0 +1,44 @@ +// +// Grid3DOverlay.h +// interface/src/ui/overlays +// +// Created by Ryan Huffman on 11/06/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_Grid3DOverlay_h +#define hifi_Grid3DOverlay_h + +// include this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + +#include + +#include +#include + +#include "Base3DOverlay.h" + +#include "renderer/ProgramObject.h" + +class Grid3DOverlay : public Base3DOverlay { + Q_OBJECT + +public: + Grid3DOverlay(); + ~Grid3DOverlay(); + + virtual void render(RenderArgs* args); + virtual void setProperties(const QScriptValue& properties); + +private: + float _minorGridWidth; + int _majorGridEvery; + + static ProgramObject _gridProgram; +}; + +#endif // hifi_Grid3DOverlay_h diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index df7a5fbcea..0192f9c216 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -23,6 +23,7 @@ #include "Overlays.h" #include "Rectangle3DOverlay.h" #include "Sphere3DOverlay.h" +#include "Grid3DOverlay.h" #include "TextOverlay.h" #include "Text3DOverlay.h" @@ -155,6 +156,8 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope thisOverlay = new Rectangle3DOverlay(); } else if (type == "line3d") { thisOverlay = new Line3DOverlay(); + } else if (type == "grid") { + thisOverlay = new Grid3DOverlay(); } else if (type == "localvoxels") { thisOverlay = new LocalVoxelsOverlay(); } else if (type == "localmodels") { diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 7445822f55..40602645f1 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); @@ -636,7 +641,7 @@ void ScriptEngine::stopTimer(QTimer *timer) { } } -QUrl ScriptEngine::resolveInclude(const QString& include) const { +QUrl ScriptEngine::resolvePath(const QString& include) const { // first lets check to see if it's already a full URL QUrl url(include); if (!url.scheme().isEmpty()) { @@ -662,7 +667,7 @@ void ScriptEngine::print(const QString& message) { } void ScriptEngine::include(const QString& includeFile) { - QUrl url = resolveInclude(includeFile); + QUrl url = resolvePath(includeFile); QString includeContents; if (url.scheme() == "http" || url.scheme() == "https" || url.scheme() == "ftp") { @@ -700,7 +705,7 @@ void ScriptEngine::include(const QString& includeFile) { } void ScriptEngine::load(const QString& loadFile) { - QUrl url = resolveInclude(loadFile); + QUrl url = resolvePath(loadFile); emit loadScript(url.toString(), false); } diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index f96b4655a9..bb279b8887 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; } @@ -103,6 +104,7 @@ public slots: void include(const QString& includeFile); void load(const QString& loadfile); void print(const QString& message); + QUrl resolvePath(const QString& path) const; void nodeKilled(SharedNodePointer node); @@ -131,7 +133,6 @@ protected: int _numAvatarSoundSentBytes; private: - QUrl resolveInclude(const QString& include) const; void sendAvatarIdentityPacket(); void sendAvatarBillboardPacket();