mirror of
https://github.com/overte-org/overte.git
synced 2025-07-23 07:23:51 +02:00
Merge pull request #7110 from ericrius1/particlesInEdit2
Particles in edit
This commit is contained in:
commit
f2445e81e0
7 changed files with 647 additions and 760 deletions
110
examples/edit.js
110
examples/edit.js
|
@ -26,6 +26,7 @@ Script.include([
|
||||||
"libraries/entityCameraTool.js",
|
"libraries/entityCameraTool.js",
|
||||||
"libraries/gridTool.js",
|
"libraries/gridTool.js",
|
||||||
"libraries/entityList.js",
|
"libraries/entityList.js",
|
||||||
|
"particle_explorer/particleExplorerTool.js",
|
||||||
"libraries/lightOverlayManager.js",
|
"libraries/lightOverlayManager.js",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -37,17 +38,13 @@ var lightOverlayManager = new LightOverlayManager();
|
||||||
var cameraManager = new CameraManager();
|
var cameraManager = new CameraManager();
|
||||||
|
|
||||||
var grid = Grid();
|
var grid = Grid();
|
||||||
// gridTool = GridTool({
|
|
||||||
// horizontalGrid: grid
|
|
||||||
// });
|
|
||||||
// gridTool.setVisible(false);
|
|
||||||
|
|
||||||
var entityListTool = EntityListTool();
|
var entityListTool = EntityListTool();
|
||||||
|
|
||||||
selectionManager.addEventListener(function() {
|
selectionManager.addEventListener(function() {
|
||||||
selectionDisplay.updateHandles();
|
selectionDisplay.updateHandles();
|
||||||
lightOverlayManager.updatePositions();
|
lightOverlayManager.updatePositions();
|
||||||
});
|
});
|
||||||
|
|
||||||
var toolIconUrl = HIFI_PUBLIC_BUCKET + "images/tools/";
|
var toolIconUrl = HIFI_PUBLIC_BUCKET + "images/tools/";
|
||||||
var toolHeight = 50;
|
var toolHeight = 50;
|
||||||
|
@ -134,9 +131,9 @@ var importingSVOTextOverlay = Overlays.addOverlay("text", {
|
||||||
|
|
||||||
var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace";
|
var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace";
|
||||||
var marketplaceWindow = new OverlayWebWindow({
|
var marketplaceWindow = new OverlayWebWindow({
|
||||||
title: 'Marketplace',
|
title: 'Marketplace',
|
||||||
source: "about:blank",
|
source: "about:blank",
|
||||||
width: 900,
|
width: 900,
|
||||||
height: 700,
|
height: 700,
|
||||||
visible: false
|
visible: false
|
||||||
});
|
});
|
||||||
|
@ -176,7 +173,8 @@ var toolBar = (function() {
|
||||||
newTextButton,
|
newTextButton,
|
||||||
newWebButton,
|
newWebButton,
|
||||||
newZoneButton,
|
newZoneButton,
|
||||||
newPolyVoxButton;
|
newPolyVoxButton,
|
||||||
|
newParticleButton
|
||||||
|
|
||||||
function initialize() {
|
function initialize() {
|
||||||
toolBar = new ToolBar(0, 0, ToolBar.VERTICAL, "highfidelity.edit.toolbar", function(windowDimensions, toolbar) {
|
toolBar = new ToolBar(0, 0, ToolBar.VERTICAL, "highfidelity.edit.toolbar", function(windowDimensions, toolbar) {
|
||||||
|
@ -186,7 +184,7 @@ var toolBar = (function() {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
activeButton = toolBar.addTool({
|
activeButton = toolBar.addTool({
|
||||||
imageURL: toolIconUrl + "edit-status.svg",
|
imageURL: toolIconUrl + "edit-status.svg",
|
||||||
|
@ -314,6 +312,20 @@ var toolBar = (function() {
|
||||||
visible: false
|
visible: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
newParticleButton = toolBar.addTool({
|
||||||
|
imageURL: toolIconUrl + "particle.svg",
|
||||||
|
subImage: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 256,
|
||||||
|
height: 256
|
||||||
|
},
|
||||||
|
width: toolWidth,
|
||||||
|
height: toolHeight,
|
||||||
|
alpha: 0.9,
|
||||||
|
visible: false
|
||||||
|
});
|
||||||
|
|
||||||
that.setActive(false);
|
that.setActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,6 +372,7 @@ var toolBar = (function() {
|
||||||
toolBar.showTool(newWebButton, doShow);
|
toolBar.showTool(newWebButton, doShow);
|
||||||
toolBar.showTool(newZoneButton, doShow);
|
toolBar.showTool(newZoneButton, doShow);
|
||||||
toolBar.showTool(newPolyVoxButton, doShow);
|
toolBar.showTool(newPolyVoxButton, doShow);
|
||||||
|
toolBar.showTool(newParticleButton, doShow);
|
||||||
};
|
};
|
||||||
|
|
||||||
var RESIZE_INTERVAL = 50;
|
var RESIZE_INTERVAL = 50;
|
||||||
|
@ -427,8 +440,8 @@ var toolBar = (function() {
|
||||||
newModelButtonDown = true;
|
newModelButtonDown = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (newCubeButton === toolBar.clicked(clickedOverlay)) {
|
if (newCubeButton === toolBar.clicked(clickedOverlay)) {
|
||||||
createNewEntity({
|
createNewEntity({
|
||||||
type: "Box",
|
type: "Box",
|
||||||
|
@ -616,6 +629,22 @@ var toolBar = (function() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (newParticleButton === toolBar.clicked(clickedOverlay)) {
|
||||||
|
createNewEntity({
|
||||||
|
type: "ParticleEffect",
|
||||||
|
isEmitting: true,
|
||||||
|
particleRadius: 0.1,
|
||||||
|
emitAcceleration: {x: 0, y: -1, z: 0},
|
||||||
|
accelerationSpread: {x: 5, y: 0, z: 5},
|
||||||
|
emitSpeed: 1,
|
||||||
|
lifespan: 1,
|
||||||
|
particleRadius: 0.025,
|
||||||
|
alphaFinish: 0,
|
||||||
|
emitRate: 100,
|
||||||
|
textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -636,7 +665,7 @@ var toolBar = (function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
newModelButtonDown = false;
|
newModelButtonDown = false;
|
||||||
|
|
||||||
|
|
||||||
return handled;
|
return handled;
|
||||||
}
|
}
|
||||||
|
@ -1226,7 +1255,8 @@ function selectAllEtitiesInCurrentSelectionBox(keepIfTouching) {
|
||||||
|
|
||||||
function deleteSelectedEntities() {
|
function deleteSelectedEntities() {
|
||||||
if (SelectionManager.hasSelection()) {
|
if (SelectionManager.hasSelection()) {
|
||||||
print(" Delete Entities");
|
selectedParticleEntity = 0;
|
||||||
|
particleExplorerTool.destroyWebView();
|
||||||
SelectionManager.saveProperties();
|
SelectionManager.saveProperties();
|
||||||
var savedProperties = [];
|
var savedProperties = [];
|
||||||
for (var i = 0; i < selectionManager.selections.length; i++) {
|
for (var i = 0; i < selectionManager.selections.length; i++) {
|
||||||
|
@ -1499,8 +1529,8 @@ PropertiesTool = function(opts) {
|
||||||
|
|
||||||
var url = Script.resolvePath('html/entityProperties.html');
|
var url = Script.resolvePath('html/entityProperties.html');
|
||||||
var webView = new OverlayWebWindow({
|
var webView = new OverlayWebWindow({
|
||||||
title: 'Entity Properties',
|
title: 'Entity Properties',
|
||||||
source: url,
|
source: url,
|
||||||
toolWindow: true
|
toolWindow: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1554,8 +1584,16 @@ PropertiesTool = function(opts) {
|
||||||
} else {
|
} else {
|
||||||
if (data.properties.dynamic === false) {
|
if (data.properties.dynamic === false) {
|
||||||
// this object is leaving dynamic, so we zero its velocities
|
// this object is leaving dynamic, so we zero its velocities
|
||||||
data.properties["velocity"] = {x: 0, y: 0, z: 0};
|
data.properties["velocity"] = {
|
||||||
data.properties["angularVelocity"] = {x: 0, y: 0, z: 0};
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
z: 0
|
||||||
|
};
|
||||||
|
data.properties["angularVelocity"] = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
z: 0
|
||||||
|
};
|
||||||
}
|
}
|
||||||
if (data.properties.rotation !== undefined) {
|
if (data.properties.rotation !== undefined) {
|
||||||
var rotation = data.properties.rotation;
|
var rotation = data.properties.rotation;
|
||||||
|
@ -1838,3 +1876,39 @@ propertyMenu.onSelectMenuItem = function(name) {
|
||||||
var showMenuItem = propertyMenu.addMenuItem("Show in Marketplace");
|
var showMenuItem = propertyMenu.addMenuItem("Show in Marketplace");
|
||||||
|
|
||||||
propertiesTool = PropertiesTool();
|
propertiesTool = PropertiesTool();
|
||||||
|
var particleExplorerTool = ParticleExplorerTool();
|
||||||
|
var selectedParticleEntity = 0;
|
||||||
|
entityListTool.webView.eventBridge.webEventReceived.connect(function(data) {
|
||||||
|
var data = JSON.parse(data);
|
||||||
|
if (data.type == "selectionUpdate") {
|
||||||
|
var ids = data.entityIds;
|
||||||
|
if(ids.length === 1) {
|
||||||
|
if (Entities.getEntityProperties(ids[0], "type").type === "ParticleEffect" ) {
|
||||||
|
if (JSON.stringify(selectedParticleEntity) === JSON.stringify(ids[0])) {
|
||||||
|
// This particle entity is already selected, so return
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Destroy the old particles web view first
|
||||||
|
particleExplorerTool.destroyWebView();
|
||||||
|
particleExplorerTool.createWebView();
|
||||||
|
var properties = Entities.getEntityProperties(ids[0]);
|
||||||
|
var particleData = {
|
||||||
|
messageType: "particle_settings",
|
||||||
|
currentProperties: properties
|
||||||
|
};
|
||||||
|
selectedParticleEntity = ids[0];
|
||||||
|
particleExplorerTool.setActiveParticleEntity(ids[0]);
|
||||||
|
|
||||||
|
particleExplorerTool.webView.eventBridge.webEventReceived.connect(function(data) {
|
||||||
|
var data = JSON.parse(data);
|
||||||
|
if (data.messageType === "page_loaded") {
|
||||||
|
particleExplorerTool.webView.eventBridge.emitScriptEvent(JSON.stringify(particleData));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
selectedParticleEntity = 0;
|
||||||
|
particleExplorerTool.destroyWebView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -1500,51 +1500,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="section-header particle-section">
|
|
||||||
<label>Particle</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="particle-section property">
|
|
||||||
<span class="label">Is Emitting</span>
|
|
||||||
<span class="value">
|
|
||||||
<input type='checkbox' id="property-particle-is-emitting">
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="particle-section property">
|
|
||||||
<div class="label">Max Particles</div>
|
|
||||||
<div class="value">
|
|
||||||
<input type='number' id="property-particle-maxparticles" min="0" max="2048" step="1">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="particle-section property">
|
|
||||||
<div class="label">Particle Life Span</div>
|
|
||||||
<div class="value">
|
|
||||||
<input type='number' id="property-particle-lifespan" min="0" step="0.1">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="particle-section property">
|
|
||||||
<div class="label">Particle Emission Rate</div>
|
|
||||||
<div class="value">
|
|
||||||
<input type='number' id="property-particle-emit-rate" min="0" step="0.5">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="particle-section property">
|
|
||||||
<div class="label">Particle Radius</div>
|
|
||||||
<div class="value">
|
|
||||||
<input class="coord" type='number' id="property-particle-radius" min="0" step="0.005">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="particle-section property">
|
|
||||||
<div class="label">Textures</div>
|
|
||||||
<div class="value">
|
|
||||||
<textarea id="property-particle-textures" value=''></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="section-header light-section">
|
<div class="section-header light-section">
|
||||||
<label>Light</label>
|
<label>Light</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,12 +8,16 @@ EntityListTool = function(opts) {
|
||||||
title: 'Entities', source: url, toolWindow: true
|
title: 'Entities', source: url, toolWindow: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var searchRadius = 100;
|
var searchRadius = 100;
|
||||||
|
|
||||||
var visible = false;
|
var visible = false;
|
||||||
|
|
||||||
webView.setVisible(visible);
|
webView.setVisible(visible);
|
||||||
|
|
||||||
|
that.webView = webView;
|
||||||
|
|
||||||
that.setVisible = function(newVisible) {
|
that.setVisible = function(newVisible) {
|
||||||
visible = newVisible;
|
visible = newVisible;
|
||||||
webView.setVisible(visible);
|
webView.setVisible(visible);
|
||||||
|
@ -71,6 +75,7 @@ EntityListTool = function(opts) {
|
||||||
webView.eventBridge.emitScriptEvent(JSON.stringify(data));
|
webView.eventBridge.emitScriptEvent(JSON.stringify(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
webView.eventBridge.webEventReceived.connect(function(data) {
|
webView.eventBridge.webEventReceived.connect(function(data) {
|
||||||
data = JSON.parse(data);
|
data = JSON.parse(data);
|
||||||
if (data.type == "selectionUpdate") {
|
if (data.type == "selectionUpdate") {
|
||||||
|
|
|
@ -1,504 +0,0 @@
|
||||||
//
|
|
||||||
// main.js
|
|
||||||
//
|
|
||||||
// Created by James B. Pollack @imgntn on 9/26/2015
|
|
||||||
// Copyright 2015 High Fidelity, Inc.
|
|
||||||
// Web app side of the App - contains GUI.
|
|
||||||
// This is an example of a new, easy way to do two way bindings between dynamically created GUI and in-world entities.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
/*global window, alert, EventBridge, dat, listenForSettingsUpdates,createVec3Folder,createQuatFolder,writeVec3ToInterface,writeDataToInterface*/
|
|
||||||
|
|
||||||
var Settings = function() {
|
|
||||||
this.exportSettings = function() {
|
|
||||||
//copyExportSettingsToClipboard();
|
|
||||||
showPreselectedPrompt();
|
|
||||||
};
|
|
||||||
this.importSettings = function() {
|
|
||||||
importSettings();
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
//2-way bindings-aren't quite ready yet. see bottom of file.
|
|
||||||
var AUTO_UPDATE = false;
|
|
||||||
var UPDATE_ALL_FREQUENCY = 100;
|
|
||||||
|
|
||||||
var controllers = [];
|
|
||||||
var colorControllers = [];
|
|
||||||
var folders = [];
|
|
||||||
var gui = null;
|
|
||||||
var settings = new Settings();
|
|
||||||
var updateInterval;
|
|
||||||
|
|
||||||
var currentInputField;
|
|
||||||
var storedController;
|
|
||||||
var keysToIgnore = [
|
|
||||||
'importSettings',
|
|
||||||
'exportSettings',
|
|
||||||
'script',
|
|
||||||
'visible',
|
|
||||||
'locked',
|
|
||||||
'userData',
|
|
||||||
'position',
|
|
||||||
'dimensions',
|
|
||||||
'rotation',
|
|
||||||
'id',
|
|
||||||
'description',
|
|
||||||
'type',
|
|
||||||
'created',
|
|
||||||
'age',
|
|
||||||
'ageAsText',
|
|
||||||
'boundingBox',
|
|
||||||
'naturalDimensions',
|
|
||||||
'naturalPosition',
|
|
||||||
'velocity',
|
|
||||||
'gravity',
|
|
||||||
'acceleration',
|
|
||||||
'damping',
|
|
||||||
'restitution',
|
|
||||||
'friction',
|
|
||||||
'density',
|
|
||||||
'lifetime',
|
|
||||||
'scriptTimestamp',
|
|
||||||
'registrationPoint',
|
|
||||||
'angularVelocity',
|
|
||||||
'angularDamping',
|
|
||||||
'collisionless',
|
|
||||||
'dynamic',
|
|
||||||
'href',
|
|
||||||
'actionData',
|
|
||||||
'marketplaceID',
|
|
||||||
'collisionSoundURL',
|
|
||||||
'shapeType',
|
|
||||||
'isEmitting',
|
|
||||||
'sittingPoints',
|
|
||||||
'originalTextures',
|
|
||||||
'parentJointIndex',
|
|
||||||
'parentID'
|
|
||||||
];
|
|
||||||
|
|
||||||
var individualKeys = [];
|
|
||||||
var vec3Keys = [];
|
|
||||||
var quatKeys = [];
|
|
||||||
var colorKeys = [];
|
|
||||||
|
|
||||||
window.onload = function() {
|
|
||||||
if (typeof EventBridge !== 'undefined') {
|
|
||||||
|
|
||||||
var stringifiedData = JSON.stringify({
|
|
||||||
messageType: 'page_loaded'
|
|
||||||
});
|
|
||||||
|
|
||||||
EventBridge.emitWebEvent(
|
|
||||||
stringifiedData
|
|
||||||
);
|
|
||||||
|
|
||||||
listenForSettingsUpdates();
|
|
||||||
window.onresize = setGUIWidthToWindowWidth;
|
|
||||||
} else {
|
|
||||||
console.log('No event bridge, probably not in interface.');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function loadGUI() {
|
|
||||||
//whether or not to autoplace
|
|
||||||
gui = new dat.GUI({
|
|
||||||
autoPlace: false
|
|
||||||
});
|
|
||||||
|
|
||||||
//if not autoplacing, put gui in a custom container
|
|
||||||
if (gui.autoPlace === false) {
|
|
||||||
var customContainer = document.getElementById('my-gui-container');
|
|
||||||
customContainer.appendChild(gui.domElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
// presets for the GUI itself. a little confusing and import/export is mostly what we want to do at the moment.
|
|
||||||
// gui.remember(settings);
|
|
||||||
|
|
||||||
var keys = _.keys(settings);
|
|
||||||
|
|
||||||
_.each(keys, function(key) {
|
|
||||||
var shouldIgnore = _.contains(keysToIgnore, key);
|
|
||||||
|
|
||||||
if (shouldIgnore) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var subKeys = _.keys(settings[key]);
|
|
||||||
var hasX = _.contains(subKeys, 'x');
|
|
||||||
var hasY = _.contains(subKeys, 'y');
|
|
||||||
var hasZ = _.contains(subKeys, 'z');
|
|
||||||
var hasW = _.contains(subKeys, 'w');
|
|
||||||
var hasRed = _.contains(subKeys, 'red');
|
|
||||||
var hasGreen = _.contains(subKeys, 'green');
|
|
||||||
var hasBlue = _.contains(subKeys, 'blue');
|
|
||||||
|
|
||||||
if ((hasX && hasY && hasZ) && hasW === false) {
|
|
||||||
vec3Keys.push(key);
|
|
||||||
} else if (hasX && hasY && hasZ && hasW) {
|
|
||||||
quatKeys.push(key);
|
|
||||||
} else if (hasRed || hasGreen || hasBlue) {
|
|
||||||
colorKeys.push(key);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
individualKeys.push(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
//alphabetize our keys
|
|
||||||
individualKeys.sort();
|
|
||||||
vec3Keys.sort();
|
|
||||||
quatKeys.sort();
|
|
||||||
colorKeys.sort();
|
|
||||||
|
|
||||||
//add to gui in the order they should appear
|
|
||||||
gui.add(settings, 'importSettings');
|
|
||||||
gui.add(settings, 'exportSettings');
|
|
||||||
addIndividualKeys();
|
|
||||||
addFolders();
|
|
||||||
|
|
||||||
//set the gui width to match the web window width
|
|
||||||
gui.width = window.innerWidth;
|
|
||||||
|
|
||||||
//2-way binding stuff
|
|
||||||
// if (AUTO_UPDATE) {
|
|
||||||
// setInterval(manuallyUpdateDisplay, UPDATE_ALL_FREQUENCY);
|
|
||||||
// registerDOMElementsForListenerBlocking();
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function addIndividualKeys() {
|
|
||||||
_.each(individualKeys, function(key) {
|
|
||||||
//temporary patch for not crashing when this goes below 0
|
|
||||||
var controller;
|
|
||||||
|
|
||||||
if (key.indexOf('emitRate') > -1) {
|
|
||||||
controller = gui.add(settings, key).min(0);
|
|
||||||
} else {
|
|
||||||
controller = gui.add(settings, key);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//2-way - need to fix not being able to input exact values if constantly listening
|
|
||||||
//controller.listen();
|
|
||||||
|
|
||||||
//keep track of our controller
|
|
||||||
controllers.push(controller);
|
|
||||||
|
|
||||||
//hook into change events for this gui controller
|
|
||||||
controller.onChange(function(value) {
|
|
||||||
// Fires on every change, drag, keypress, etc.
|
|
||||||
writeDataToInterface(this.property, value);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function addFolders() {
|
|
||||||
_.each(colorKeys, function(key) {
|
|
||||||
createColorPicker(key);
|
|
||||||
});
|
|
||||||
_.each(vec3Keys, function(key) {
|
|
||||||
createVec3Folder(key);
|
|
||||||
});
|
|
||||||
_.each(quatKeys, function(key) {
|
|
||||||
createQuatFolder(key);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function createColorPicker(key) {
|
|
||||||
var colorObject = settings[key];
|
|
||||||
var colorArray = convertColorObjectToArray(colorObject);
|
|
||||||
settings[key] = colorArray;
|
|
||||||
var controller = gui.addColor(settings, key);
|
|
||||||
controller.onChange(function(value) {
|
|
||||||
var obj = {};
|
|
||||||
obj[key] = convertColorArrayToObject(value);
|
|
||||||
writeVec3ToInterface(obj);
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createVec3Folder(category) {
|
|
||||||
var folder = gui.addFolder(category);
|
|
||||||
|
|
||||||
folder.add(settings[category], 'x').step(0.1).onChange(function(value) {
|
|
||||||
// Fires when a controller loses focus.
|
|
||||||
var obj = {};
|
|
||||||
obj[category] = {};
|
|
||||||
obj[category][this.property] = value;
|
|
||||||
obj[category].y = settings[category].y;
|
|
||||||
obj[category].z = settings[category].z;
|
|
||||||
writeVec3ToInterface(obj);
|
|
||||||
});
|
|
||||||
|
|
||||||
folder.add(settings[category], 'y').step(0.1).onChange(function(value) {
|
|
||||||
// Fires when a controller loses focus.
|
|
||||||
var obj = {};
|
|
||||||
obj[category] = {};
|
|
||||||
obj[category].x = settings[category].x;
|
|
||||||
obj[category][this.property] = value;
|
|
||||||
obj[category].z = settings[category].z;
|
|
||||||
writeVec3ToInterface(obj);
|
|
||||||
});
|
|
||||||
|
|
||||||
folder.add(settings[category], 'z').step(0.1).onChange(function(value) {
|
|
||||||
// Fires when a controller loses focus.
|
|
||||||
var obj = {};
|
|
||||||
obj[category] = {};
|
|
||||||
obj[category].y = settings[category].y;
|
|
||||||
obj[category].x = settings[category].x;
|
|
||||||
obj[category][this.property] = value;
|
|
||||||
writeVec3ToInterface(obj);
|
|
||||||
});
|
|
||||||
|
|
||||||
folders.push(folder);
|
|
||||||
folder.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
function createQuatFolder(category) {
|
|
||||||
var folder = gui.addFolder(category);
|
|
||||||
|
|
||||||
folder.add(settings[category], 'x').step(0.1).onChange(function(value) {
|
|
||||||
// Fires when a controller loses focus.
|
|
||||||
var obj = {};
|
|
||||||
obj[category] = {};
|
|
||||||
obj[category][this.property] = value;
|
|
||||||
obj[category].y = settings[category].y;
|
|
||||||
obj[category].z = settings[category].z;
|
|
||||||
obj[category].w = settings[category].w;
|
|
||||||
writeVec3ToInterface(obj);
|
|
||||||
});
|
|
||||||
|
|
||||||
folder.add(settings[category], 'y').step(0.1).onChange(function(value) {
|
|
||||||
// Fires when a controller loses focus.
|
|
||||||
var obj = {};
|
|
||||||
obj[category] = {};
|
|
||||||
obj[category].x = settings[category].x;
|
|
||||||
obj[category][this.property] = value;
|
|
||||||
obj[category].z = settings[category].z;
|
|
||||||
obj[category].w = settings[category].w;
|
|
||||||
writeVec3ToInterface(obj);
|
|
||||||
});
|
|
||||||
|
|
||||||
folder.add(settings[category], 'z').step(0.1).onChange(function(value) {
|
|
||||||
// Fires when a controller loses focus.
|
|
||||||
var obj = {};
|
|
||||||
obj[category] = {};
|
|
||||||
obj[category].x = settings[category].x;
|
|
||||||
obj[category].y = settings[category].y;
|
|
||||||
obj[category][this.property] = value;
|
|
||||||
obj[category].w = settings[category].w;
|
|
||||||
writeVec3ToInterface(obj);
|
|
||||||
});
|
|
||||||
|
|
||||||
folder.add(settings[category], 'w').step(0.1).onChange(function(value) {
|
|
||||||
// Fires when a controller loses focus.
|
|
||||||
var obj = {};
|
|
||||||
obj[category] = {};
|
|
||||||
obj[category].x = settings[category].x;
|
|
||||||
obj[category].y = settings[category].y;
|
|
||||||
obj[category].z = settings[category].z;
|
|
||||||
obj[category][this.property] = value;
|
|
||||||
writeVec3ToInterface(obj);
|
|
||||||
});
|
|
||||||
|
|
||||||
folders.push(folder);
|
|
||||||
folder.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
function convertColorObjectToArray(colorObject) {
|
|
||||||
var colorArray = [];
|
|
||||||
|
|
||||||
_.each(colorObject, function(singleColor) {
|
|
||||||
colorArray.push(singleColor);
|
|
||||||
});
|
|
||||||
|
|
||||||
return colorArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
function convertColorArrayToObject(colorArray) {
|
|
||||||
var colorObject = {
|
|
||||||
red: colorArray[0],
|
|
||||||
green: colorArray[1],
|
|
||||||
blue: colorArray[2]
|
|
||||||
};
|
|
||||||
|
|
||||||
return colorObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
function writeDataToInterface(property, value) {
|
|
||||||
var data = {};
|
|
||||||
data[property] = value;
|
|
||||||
|
|
||||||
var sendData = {
|
|
||||||
messageType: "settings_update",
|
|
||||||
updatedSettings: data
|
|
||||||
};
|
|
||||||
|
|
||||||
var stringifiedData = JSON.stringify(sendData);
|
|
||||||
|
|
||||||
EventBridge.emitWebEvent(stringifiedData);
|
|
||||||
}
|
|
||||||
|
|
||||||
function writeVec3ToInterface(obj) {
|
|
||||||
var sendData = {
|
|
||||||
messageType: "settings_update",
|
|
||||||
updatedSettings: obj
|
|
||||||
};
|
|
||||||
|
|
||||||
var stringifiedData = JSON.stringify(sendData);
|
|
||||||
|
|
||||||
EventBridge.emitWebEvent(stringifiedData);
|
|
||||||
}
|
|
||||||
|
|
||||||
function listenForSettingsUpdates() {
|
|
||||||
EventBridge.scriptEventReceived.connect(function(data) {
|
|
||||||
data = JSON.parse(data);
|
|
||||||
|
|
||||||
//2-way
|
|
||||||
// if (data.messageType === 'object_update') {
|
|
||||||
// _.each(data.objectSettings, function(value, key) {
|
|
||||||
// settings[key] = value;
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (data.messageType === 'initial_settings') {
|
|
||||||
_.each(data.initialSettings, function(value, key) {
|
|
||||||
settings[key] = {};
|
|
||||||
settings[key] = value;
|
|
||||||
});
|
|
||||||
|
|
||||||
loadGUI();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function manuallyUpdateDisplay() {
|
|
||||||
// Iterate over all controllers
|
|
||||||
// this is expensive, write a method for indiviudal controllers and use it when the value is different than a cached value, perhaps.
|
|
||||||
var i;
|
|
||||||
for (i in gui.__controllers) {
|
|
||||||
gui.__controllers[i].updateDisplay();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setGUIWidthToWindowWidth() {
|
|
||||||
if (gui !== null) {
|
|
||||||
gui.width = window.innerWidth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleInputKeyPress(e) {
|
|
||||||
if (e.keyCode === 13) {
|
|
||||||
importSettings();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function importSettings() {
|
|
||||||
var importInput = document.getElementById('importer-input');
|
|
||||||
|
|
||||||
try {
|
|
||||||
var importedSettings = JSON.parse(importInput.value);
|
|
||||||
|
|
||||||
var keys = _.keys(importedSettings);
|
|
||||||
|
|
||||||
_.each(keys, function(key) {
|
|
||||||
var shouldIgnore = _.contains(keysToIgnore, key);
|
|
||||||
|
|
||||||
if (shouldIgnore) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
settings[key] = importedSettings[key];
|
|
||||||
});
|
|
||||||
|
|
||||||
writeVec3ToInterface(settings);
|
|
||||||
|
|
||||||
manuallyUpdateDisplay();
|
|
||||||
} catch (e) {
|
|
||||||
alert('Not properly formatted JSON');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function prepareSettingsForExport() {
|
|
||||||
var keys = _.keys(settings);
|
|
||||||
|
|
||||||
var exportSettings = {};
|
|
||||||
|
|
||||||
_.each(keys, function(key) {
|
|
||||||
var shouldIgnore = _.contains(keysToIgnore, key);
|
|
||||||
|
|
||||||
if (shouldIgnore) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key.indexOf('color') > -1) {
|
|
||||||
var colorObject = convertColorArrayToObject(settings[key]);
|
|
||||||
settings[key] = colorObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
exportSettings[key] = settings[key];
|
|
||||||
});
|
|
||||||
|
|
||||||
return JSON.stringify(exportSettings);
|
|
||||||
}
|
|
||||||
|
|
||||||
function showPreselectedPrompt() {
|
|
||||||
window.prompt("Ctrl-C to copy, then Enter.", prepareSettingsForExport());
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeContainerDomElement() {
|
|
||||||
var elem = document.getElementById("my-gui-container");
|
|
||||||
elem.parentNode.removeChild(elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeListenerFromGUI(key) {
|
|
||||||
_.each(gui.__listening, function(controller, index) {
|
|
||||||
if (controller.property === key) {
|
|
||||||
storedController = controller;
|
|
||||||
gui.__listening.splice(index, 1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//the section below is to try to work at achieving two way bindings;
|
|
||||||
function addListenersBackToGUI() {
|
|
||||||
gui.__listening.push(storedController);
|
|
||||||
storedController = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function registerDOMElementsForListenerBlocking() {
|
|
||||||
_.each(gui.__controllers, function(controller) {
|
|
||||||
var input = controller.domElement.childNodes[0];
|
|
||||||
input.addEventListener('focus', function() {
|
|
||||||
console.log('INPUT ELEMENT GOT FOCUS!' + controller.property);
|
|
||||||
removeListenerFromGUI(controller.property);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
_.each(gui.__controllers, function(controller) {
|
|
||||||
var input = controller.domElement.childNodes[0];
|
|
||||||
input.addEventListener('blur', function() {
|
|
||||||
console.log('INPUT ELEMENT GOT BLUR!' + controller.property);
|
|
||||||
addListenersBackToGUI();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// also listen to inputs inside of folders
|
|
||||||
_.each(gui.__folders, function(folder) {
|
|
||||||
_.each(folder.__controllers, function(controller) {
|
|
||||||
var input = controller.__input;
|
|
||||||
input.addEventListener('focus', function() {
|
|
||||||
console.log('FOLDER ELEMENT GOT FOCUS!' + controller.property);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!--
|
<!--
|
||||||
// main.js
|
// particleExplorer.hml
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Created by James B. Pollack @imgntn on 9/26/2015
|
// Created by James B. Pollack @imgntn on 9/26/2015
|
||||||
|
@ -16,14 +16,16 @@
|
||||||
<head>
|
<head>
|
||||||
<script type="text/javascript" src="dat.gui.min.js"></script>
|
<script type="text/javascript" src="dat.gui.min.js"></script>
|
||||||
<script type="text/javascript" src="underscore-min.js"></script>
|
<script type="text/javascript" src="underscore-min.js"></script>
|
||||||
<script type="text/javascript" src="main.js?123"></script>
|
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||||
|
<script type="text/javascript" src="../html/eventBridgeLoader.js"></script>
|
||||||
|
<script type="text/javascript" src="particleExplorer.js?v42"></script>
|
||||||
<script>
|
<script>
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
|
|
||||||
body{
|
body{
|
||||||
background-color:black;
|
background-color:black;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
#my-gui-container{
|
#my-gui-container{
|
||||||
|
@ -31,12 +33,23 @@ body{
|
||||||
}
|
}
|
||||||
|
|
||||||
.importer{
|
.importer{
|
||||||
margin-bottom:4px;
|
margin-bottom:4px;
|
||||||
|
}
|
||||||
|
.exported-props-section {
|
||||||
|
width: 50%;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
}
|
||||||
|
#exported-props {
|
||||||
|
/* Set the margin-left and margin-right automatically set */
|
||||||
|
color: white;
|
||||||
|
white-space: pre-wrap; /* css-3 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
::-webkit-input-placeholder {
|
::-webkit-input-placeholder {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-family: Helvetica
|
font-family: Helvetica
|
||||||
}
|
}
|
||||||
|
|
||||||
#importer-input{
|
#importer-input{
|
||||||
|
@ -50,6 +63,8 @@ body{
|
||||||
<body>
|
<body>
|
||||||
<div class="importer">
|
<div class="importer">
|
||||||
<input type='text' id="importer-input" placeholder="Import: Paste JSON here." onkeypress="handleInputKeyPress(event)">
|
<input type='text' id="importer-input" placeholder="Import: Paste JSON here." onkeypress="handleInputKeyPress(event)">
|
||||||
|
<div class = "exported-props-section">
|
||||||
|
<div id = "exported-props"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="my-gui-container">
|
<div id="my-gui-container">
|
||||||
</div>
|
</div>
|
|
@ -2,215 +2,497 @@
|
||||||
// particleExplorer.js
|
// particleExplorer.js
|
||||||
//
|
//
|
||||||
// Created by James B. Pollack @imgntn on 9/26/2015
|
// Created by James B. Pollack @imgntn on 9/26/2015
|
||||||
// includes setup from @ctrlaltdavid's particlesTest.js
|
|
||||||
// Copyright 2015 High Fidelity, Inc.
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
//
|
// Web app side of the App - contains GUI.
|
||||||
// Interface side of the App.
|
// This is an example of a new, easy way to do two way bindings between dynamically created GUI and in-world entities.
|
||||||
// Quickly edit the aesthetics of a particle system.
|
|
||||||
//
|
//
|
||||||
// Distributed under the Apache License, Version 2.0.
|
// Distributed under the Apache License, Version 2.0.
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
// next version: 2 way bindings, integrate with edit.js
|
/*global window, alert, EventBridge, dat, listenForSettingsUpdates,createVec3Folder,createQuatFolder,writeVec3ToInterface,writeDataToInterface*/
|
||||||
//
|
|
||||||
/*global print, WebWindow, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
|
|
||||||
|
|
||||||
var box,
|
var Settings = function() {
|
||||||
sphere,
|
this.exportSettings = function() {
|
||||||
sphereDimensions = {
|
//copyExportSettingsToClipboard();
|
||||||
x: 0.4,
|
showPreselectedPrompt();
|
||||||
y: 0.8,
|
|
||||||
z: 0.2
|
|
||||||
},
|
|
||||||
pointDimensions = {
|
|
||||||
x: 0.0,
|
|
||||||
y: 0.0,
|
|
||||||
z: 0.0
|
|
||||||
},
|
|
||||||
sphereOrientation = Quat.fromPitchYawRollDegrees(-60.0, 30.0, 0.0),
|
|
||||||
verticalOrientation = Quat.fromPitchYawRollDegrees(-90.0, 0.0, 0.0),
|
|
||||||
particles,
|
|
||||||
particleExample = -1,
|
|
||||||
PARTICLE_RADIUS = 0.04,
|
|
||||||
SLOW_EMIT_RATE = 2.0,
|
|
||||||
HALF_EMIT_RATE = 50.0,
|
|
||||||
FAST_EMIT_RATE = 100.0,
|
|
||||||
SLOW_EMIT_SPEED = 0.025,
|
|
||||||
FAST_EMIT_SPEED = 1.0,
|
|
||||||
GRAVITY_EMIT_ACCELERATON = {
|
|
||||||
x: 0.0,
|
|
||||||
y: -0.3,
|
|
||||||
z: 0.0
|
|
||||||
},
|
|
||||||
ZERO_EMIT_ACCELERATON = {
|
|
||||||
x: 0.0,
|
|
||||||
y: 0.0,
|
|
||||||
z: 0.0
|
|
||||||
},
|
|
||||||
PI = 3.141593,
|
|
||||||
DEG_TO_RAD = PI / 180.0,
|
|
||||||
NUM_PARTICLE_EXAMPLES = 18;
|
|
||||||
|
|
||||||
var particleProperties;
|
|
||||||
|
|
||||||
function setUp() {
|
|
||||||
var boxPoint,
|
|
||||||
spawnPoint;
|
|
||||||
|
|
||||||
boxPoint = Vec3.sum(MyAvatar.position, Vec3.multiply(4.0, Quat.getFront(Camera.getOrientation())));
|
|
||||||
boxPoint = Vec3.sum(boxPoint, {
|
|
||||||
x: 0.0,
|
|
||||||
y: -0.5,
|
|
||||||
z: 0.0
|
|
||||||
});
|
|
||||||
spawnPoint = Vec3.sum(boxPoint, {
|
|
||||||
x: 0.0,
|
|
||||||
y: 1.0,
|
|
||||||
z: 0.0
|
|
||||||
});
|
|
||||||
|
|
||||||
box = Entities.addEntity({
|
|
||||||
type: "Box",
|
|
||||||
name: "ParticlesTest Box",
|
|
||||||
position: boxPoint,
|
|
||||||
rotation: verticalOrientation,
|
|
||||||
dimensions: {
|
|
||||||
x: 0.3,
|
|
||||||
y: 0.3,
|
|
||||||
z: 0.3
|
|
||||||
},
|
|
||||||
color: {
|
|
||||||
red: 128,
|
|
||||||
green: 128,
|
|
||||||
blue: 128
|
|
||||||
},
|
|
||||||
lifetime: 3600, // 1 hour; just in case
|
|
||||||
visible: true
|
|
||||||
});
|
|
||||||
|
|
||||||
// Same size and orientation as emitter when ellipsoid.
|
|
||||||
sphere = Entities.addEntity({
|
|
||||||
type: "Sphere",
|
|
||||||
name: "ParticlesTest Sphere",
|
|
||||||
position: boxPoint,
|
|
||||||
rotation: sphereOrientation,
|
|
||||||
dimensions: sphereDimensions,
|
|
||||||
color: {
|
|
||||||
red: 128,
|
|
||||||
green: 128,
|
|
||||||
blue: 128
|
|
||||||
},
|
|
||||||
lifetime: 3600, // 1 hour; just in case
|
|
||||||
visible: false
|
|
||||||
});
|
|
||||||
|
|
||||||
// 1.0m above the box or ellipsoid.
|
|
||||||
particles = Entities.addEntity({
|
|
||||||
type: "ParticleEffect",
|
|
||||||
name: "ParticlesTest Emitter",
|
|
||||||
position: spawnPoint,
|
|
||||||
emitOrientation: verticalOrientation,
|
|
||||||
particleRadius: PARTICLE_RADIUS,
|
|
||||||
radiusSpread: 0.0,
|
|
||||||
emitRate: SLOW_EMIT_RATE,
|
|
||||||
emitSpeed: FAST_EMIT_SPEED,
|
|
||||||
speedSpread: 0.0,
|
|
||||||
emitAcceleration: GRAVITY_EMIT_ACCELERATON,
|
|
||||||
accelerationSpread: {
|
|
||||||
x: 0.0,
|
|
||||||
y: 0.0,
|
|
||||||
z: 0.0
|
|
||||||
},
|
|
||||||
textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png",
|
|
||||||
color: {
|
|
||||||
red: 255,
|
|
||||||
green: 255,
|
|
||||||
blue: 255
|
|
||||||
},
|
|
||||||
lifespan: 5.0,
|
|
||||||
locked: false,
|
|
||||||
isEmitting: true,
|
|
||||||
lifetime: 3600 // 1 hour; just in case
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsWindow = function() {
|
|
||||||
var _this = this;
|
|
||||||
|
|
||||||
this.webWindow = null;
|
|
||||||
|
|
||||||
this.init = function() {
|
|
||||||
Script.update.connect(waitForObjectAuthorization);
|
|
||||||
_this.webWindow = new WebWindow('Particle Explorer', Script.resolvePath('index.html'), 400, 600, false);
|
|
||||||
_this.webWindow.eventBridge.webEventReceived.connect(_this.onWebEventReceived);
|
|
||||||
};
|
};
|
||||||
|
this.importSettings = function() {
|
||||||
this.sendData = function(data) {
|
importSettings();
|
||||||
_this.webWindow.eventBridge.emitScriptEvent(JSON.stringify(data));
|
|
||||||
};
|
|
||||||
|
|
||||||
this.onWebEventReceived = function(data) {
|
|
||||||
var _data = JSON.parse(data);
|
|
||||||
if (_data.messageType === 'page_loaded') {
|
|
||||||
_this.webWindow.setVisible(true);
|
|
||||||
_this.pageLoaded = true;
|
|
||||||
sendInitialSettings(particleProperties);
|
|
||||||
}
|
|
||||||
if (_data.messageType === 'settings_update') {
|
|
||||||
editEntity(_data.updatedSettings);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
function waitForObjectAuthorization() {
|
//2-way bindings-aren't quite ready yet. see bottom of file.
|
||||||
var properties = Entities.getEntityProperties(particles, "isKnownID");
|
var AUTO_UPDATE = false;
|
||||||
var isKnownID = properties.isKnownID;
|
var UPDATE_ALL_FREQUENCY = 100;
|
||||||
if (isKnownID === false || SettingsWindow.pageLoaded === false) {
|
|
||||||
return;
|
var controllers = [];
|
||||||
|
var colorControllers = [];
|
||||||
|
var folders = [];
|
||||||
|
var gui = null;
|
||||||
|
var settings = new Settings();
|
||||||
|
var updateInterval;
|
||||||
|
|
||||||
|
var currentInputField;
|
||||||
|
var storedController;
|
||||||
|
//CHANGE TO WHITELIST
|
||||||
|
var keysToAllow = [
|
||||||
|
'isEmitting',
|
||||||
|
'maxParticles',
|
||||||
|
'lifespan',
|
||||||
|
'emitRate',
|
||||||
|
'emitSpeed',
|
||||||
|
'speedSpread',
|
||||||
|
'emitOrientation',
|
||||||
|
'emitDimensios',
|
||||||
|
'emitRadiusStart',
|
||||||
|
'polarStart',
|
||||||
|
'polarFinish',
|
||||||
|
'azimuthFinish',
|
||||||
|
'emitAcceleration',
|
||||||
|
'accelerationSpread',
|
||||||
|
'particleRadius',
|
||||||
|
'radiusSpread',
|
||||||
|
'radiusStart',
|
||||||
|
'radiusFinish',
|
||||||
|
'color',
|
||||||
|
'colorSpread',
|
||||||
|
'colorStart',
|
||||||
|
'colorFinish',
|
||||||
|
'alpha',
|
||||||
|
'alphaSpread',
|
||||||
|
'alphaStart',
|
||||||
|
'alphaFinish',
|
||||||
|
'emitterShouldTrail',
|
||||||
|
'textures'
|
||||||
|
];
|
||||||
|
|
||||||
|
var individualKeys = [];
|
||||||
|
var vec3Keys = [];
|
||||||
|
var quatKeys = [];
|
||||||
|
var colorKeys = [];
|
||||||
|
|
||||||
|
window.onload = function() {
|
||||||
|
|
||||||
|
openEventBridge(function() {
|
||||||
|
var stringifiedData = JSON.stringify({
|
||||||
|
messageType: 'page_loaded'
|
||||||
|
});
|
||||||
|
|
||||||
|
EventBridge.emitWebEvent(
|
||||||
|
stringifiedData
|
||||||
|
);
|
||||||
|
|
||||||
|
listenForSettingsUpdates();
|
||||||
|
window.onresize = setGUIWidthToWindowWidth;
|
||||||
|
})
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
function loadGUI() {
|
||||||
|
//whether or not to autoplace
|
||||||
|
gui = new dat.GUI({
|
||||||
|
autoPlace: false
|
||||||
|
});
|
||||||
|
|
||||||
|
//if not autoplacing, put gui in a custom container
|
||||||
|
if (gui.autoPlace === false) {
|
||||||
|
var customContainer = document.getElementById('my-gui-container');
|
||||||
|
customContainer.appendChild(gui.domElement);
|
||||||
}
|
}
|
||||||
var currentProperties = Entities.getEntityProperties(particles);
|
|
||||||
particleProperties = currentProperties;
|
// presets for the GUI itself. a little confusing and import/export is mostly what we want to do at the moment.
|
||||||
Script.update.connect(sendObjectUpdates);
|
// gui.remember(settings);
|
||||||
Script.update.disconnect(waitForObjectAuthorization);
|
|
||||||
|
var keys = _.keys(settings);
|
||||||
|
|
||||||
|
_.each(keys, function(key) {
|
||||||
|
var shouldAllow = _.contains(keysToAllow, key);
|
||||||
|
|
||||||
|
if (shouldAllow) {
|
||||||
|
var subKeys = _.keys(settings[key]);
|
||||||
|
var hasX = _.contains(subKeys, 'x');
|
||||||
|
var hasY = _.contains(subKeys, 'y');
|
||||||
|
var hasZ = _.contains(subKeys, 'z');
|
||||||
|
var hasW = _.contains(subKeys, 'w');
|
||||||
|
var hasRed = _.contains(subKeys, 'red');
|
||||||
|
var hasGreen = _.contains(subKeys, 'green');
|
||||||
|
var hasBlue = _.contains(subKeys, 'blue');
|
||||||
|
|
||||||
|
if ((hasX && hasY && hasZ) && hasW === false) {
|
||||||
|
vec3Keys.push(key);
|
||||||
|
} else if (hasX && hasY && hasZ && hasW) {
|
||||||
|
quatKeys.push(key);
|
||||||
|
} else if (hasRed || hasGreen || hasBlue) {
|
||||||
|
colorKeys.push(key);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
individualKeys.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//alphabetize our keys
|
||||||
|
individualKeys.sort();
|
||||||
|
vec3Keys.sort();
|
||||||
|
quatKeys.sort();
|
||||||
|
colorKeys.sort();
|
||||||
|
|
||||||
|
//add to gui in the order they should appear
|
||||||
|
gui.add(settings, 'importSettings');
|
||||||
|
gui.add(settings, 'exportSettings');
|
||||||
|
addIndividualKeys();
|
||||||
|
addFolders();
|
||||||
|
|
||||||
|
//set the gui width to match the web window width
|
||||||
|
gui.width = window.innerWidth;
|
||||||
|
|
||||||
|
//2-way binding stuff
|
||||||
|
// if (AUTO_UPDATE) {
|
||||||
|
// setInterval(manuallyUpdateDisplay, UPDATE_ALL_FREQUENCY);
|
||||||
|
// registerDOMElementsForListenerBlocking();
|
||||||
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendObjectUpdates() {
|
function addIndividualKeys() {
|
||||||
var currentProperties = Entities.getEntityProperties(particles);
|
_.each(individualKeys, function(key) {
|
||||||
sendUpdatedObject(currentProperties);
|
//temporary patch for not crashing when this goes below 0
|
||||||
|
var controller;
|
||||||
|
|
||||||
|
if (key.indexOf('emitRate') > -1) {
|
||||||
|
controller = gui.add(settings, key).min(0);
|
||||||
|
} else {
|
||||||
|
controller = gui.add(settings, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
//2-way - need to fix not being able to input exact values if constantly listening
|
||||||
|
//controller.listen();
|
||||||
|
|
||||||
|
//keep track of our controller
|
||||||
|
controllers.push(controller);
|
||||||
|
|
||||||
|
//hook into change events for this gui controller
|
||||||
|
controller.onChange(function(value) {
|
||||||
|
// Fires on every change, drag, keypress, etc.
|
||||||
|
writeDataToInterface(this.property, value);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendInitialSettings(properties) {
|
function addFolders() {
|
||||||
var settings = {
|
_.each(colorKeys, function(key) {
|
||||||
messageType: 'initial_settings',
|
createColorPicker(key);
|
||||||
initialSettings: properties
|
});
|
||||||
|
_.each(vec3Keys, function(key) {
|
||||||
|
createVec3Folder(key);
|
||||||
|
});
|
||||||
|
_.each(quatKeys, function(key) {
|
||||||
|
createQuatFolder(key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createColorPicker(key) {
|
||||||
|
var colorObject = settings[key];
|
||||||
|
var colorArray = convertColorObjectToArray(colorObject);
|
||||||
|
settings[key] = colorArray;
|
||||||
|
var controller = gui.addColor(settings, key);
|
||||||
|
controller.onChange(function(value) {
|
||||||
|
var obj = {};
|
||||||
|
obj[key] = convertColorArrayToObject(value);
|
||||||
|
writeVec3ToInterface(obj);
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createVec3Folder(category) {
|
||||||
|
var folder = gui.addFolder(category);
|
||||||
|
|
||||||
|
folder.add(settings[category], 'x').step(0.1).onChange(function(value) {
|
||||||
|
// Fires when a controller loses focus.
|
||||||
|
var obj = {};
|
||||||
|
obj[category] = {};
|
||||||
|
obj[category][this.property] = value;
|
||||||
|
obj[category].y = settings[category].y;
|
||||||
|
obj[category].z = settings[category].z;
|
||||||
|
writeVec3ToInterface(obj);
|
||||||
|
});
|
||||||
|
|
||||||
|
folder.add(settings[category], 'y').step(0.1).onChange(function(value) {
|
||||||
|
// Fires when a controller loses focus.
|
||||||
|
var obj = {};
|
||||||
|
obj[category] = {};
|
||||||
|
obj[category].x = settings[category].x;
|
||||||
|
obj[category][this.property] = value;
|
||||||
|
obj[category].z = settings[category].z;
|
||||||
|
writeVec3ToInterface(obj);
|
||||||
|
});
|
||||||
|
|
||||||
|
folder.add(settings[category], 'z').step(0.1).onChange(function(value) {
|
||||||
|
// Fires when a controller loses focus.
|
||||||
|
var obj = {};
|
||||||
|
obj[category] = {};
|
||||||
|
obj[category].y = settings[category].y;
|
||||||
|
obj[category].x = settings[category].x;
|
||||||
|
obj[category][this.property] = value;
|
||||||
|
writeVec3ToInterface(obj);
|
||||||
|
});
|
||||||
|
|
||||||
|
folders.push(folder);
|
||||||
|
folder.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
function createQuatFolder(category) {
|
||||||
|
var folder = gui.addFolder(category);
|
||||||
|
|
||||||
|
folder.add(settings[category], 'x').step(0.1).onChange(function(value) {
|
||||||
|
// Fires when a controller loses focus.
|
||||||
|
var obj = {};
|
||||||
|
obj[category] = {};
|
||||||
|
obj[category][this.property] = value;
|
||||||
|
obj[category].y = settings[category].y;
|
||||||
|
obj[category].z = settings[category].z;
|
||||||
|
obj[category].w = settings[category].w;
|
||||||
|
writeVec3ToInterface(obj);
|
||||||
|
});
|
||||||
|
|
||||||
|
folder.add(settings[category], 'y').step(0.1).onChange(function(value) {
|
||||||
|
// Fires when a controller loses focus.
|
||||||
|
var obj = {};
|
||||||
|
obj[category] = {};
|
||||||
|
obj[category].x = settings[category].x;
|
||||||
|
obj[category][this.property] = value;
|
||||||
|
obj[category].z = settings[category].z;
|
||||||
|
obj[category].w = settings[category].w;
|
||||||
|
writeVec3ToInterface(obj);
|
||||||
|
});
|
||||||
|
|
||||||
|
folder.add(settings[category], 'z').step(0.1).onChange(function(value) {
|
||||||
|
// Fires when a controller loses focus.
|
||||||
|
var obj = {};
|
||||||
|
obj[category] = {};
|
||||||
|
obj[category].x = settings[category].x;
|
||||||
|
obj[category].y = settings[category].y;
|
||||||
|
obj[category][this.property] = value;
|
||||||
|
obj[category].w = settings[category].w;
|
||||||
|
writeVec3ToInterface(obj);
|
||||||
|
});
|
||||||
|
|
||||||
|
folder.add(settings[category], 'w').step(0.1).onChange(function(value) {
|
||||||
|
// Fires when a controller loses focus.
|
||||||
|
var obj = {};
|
||||||
|
obj[category] = {};
|
||||||
|
obj[category].x = settings[category].x;
|
||||||
|
obj[category].y = settings[category].y;
|
||||||
|
obj[category].z = settings[category].z;
|
||||||
|
obj[category][this.property] = value;
|
||||||
|
writeVec3ToInterface(obj);
|
||||||
|
});
|
||||||
|
|
||||||
|
folders.push(folder);
|
||||||
|
folder.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertColorObjectToArray(colorObject) {
|
||||||
|
var colorArray = [];
|
||||||
|
|
||||||
|
_.each(colorObject, function(singleColor) {
|
||||||
|
colorArray.push(singleColor);
|
||||||
|
});
|
||||||
|
|
||||||
|
return colorArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertColorArrayToObject(colorArray) {
|
||||||
|
var colorObject = {
|
||||||
|
red: colorArray[0],
|
||||||
|
green: colorArray[1],
|
||||||
|
blue: colorArray[2]
|
||||||
};
|
};
|
||||||
|
|
||||||
settingsWindow.sendData(settings);
|
return colorObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendUpdatedObject(properties) {
|
function writeDataToInterface(property, value) {
|
||||||
var settings = {
|
var data = {};
|
||||||
messageType: 'object_update',
|
data[property] = value;
|
||||||
objectSettings: properties
|
|
||||||
|
var sendData = {
|
||||||
|
messageType: "settings_update",
|
||||||
|
updatedSettings: data
|
||||||
};
|
};
|
||||||
settingsWindow.sendData(settings);
|
|
||||||
|
var stringifiedData = JSON.stringify(sendData);
|
||||||
|
|
||||||
|
EventBridge.emitWebEvent(stringifiedData);
|
||||||
}
|
}
|
||||||
|
|
||||||
function editEntity(properties) {
|
function writeVec3ToInterface(obj) {
|
||||||
Entities.editEntity(particles, properties);
|
var sendData = {
|
||||||
|
messageType: "settings_update",
|
||||||
|
updatedSettings: obj
|
||||||
|
};
|
||||||
|
|
||||||
|
var stringifiedData = JSON.stringify(sendData);
|
||||||
|
|
||||||
|
EventBridge.emitWebEvent(stringifiedData);
|
||||||
}
|
}
|
||||||
|
|
||||||
function tearDown() {
|
function listenForSettingsUpdates() {
|
||||||
Entities.deleteEntity(particles);
|
EventBridge.scriptEventReceived.connect(function(data) {
|
||||||
Entities.deleteEntity(box);
|
data = JSON.parse(data);
|
||||||
Entities.deleteEntity(sphere);
|
if (data.messageType === 'particle_settings') {
|
||||||
Script.update.disconnect(sendObjectUpdates);
|
_.each(data.currentProperties, function(value, key) {
|
||||||
|
settings[key] = {};
|
||||||
|
settings[key] = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
loadGUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var settingsWindow = new SettingsWindow();
|
function manuallyUpdateDisplay() {
|
||||||
settingsWindow.init();
|
// Iterate over all controllers
|
||||||
setUp();
|
// this is expensive, write a method for indiviudal controllers and use it when the value is different than a cached value, perhaps.
|
||||||
Script.scriptEnding.connect(tearDown);
|
var i;
|
||||||
|
for (i in gui.__controllers) {
|
||||||
|
gui.__controllers[i].updateDisplay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setGUIWidthToWindowWidth() {
|
||||||
|
if (gui !== null) {
|
||||||
|
gui.width = window.innerWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleInputKeyPress(e) {
|
||||||
|
if (e.keyCode === 13) {
|
||||||
|
importSettings();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function importSettings() {
|
||||||
|
var importInput = document.getElementById('importer-input');
|
||||||
|
|
||||||
|
try {
|
||||||
|
var importedSettings = JSON.parse(importInput.value);
|
||||||
|
|
||||||
|
var keys = _.keys(importedSettings);
|
||||||
|
|
||||||
|
_.each(keys, function(key) {
|
||||||
|
var shouldAllow = _.contains(keysToAllow, key);
|
||||||
|
|
||||||
|
if (!shouldAllow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
settings[key] = importedSettings[key];
|
||||||
|
});
|
||||||
|
|
||||||
|
writeVec3ToInterface(settings);
|
||||||
|
|
||||||
|
manuallyUpdateDisplay();
|
||||||
|
} catch (e) {
|
||||||
|
alert('Not properly formatted JSON');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function prepareSettingsForExport() {
|
||||||
|
var keys = _.keys(settings);
|
||||||
|
|
||||||
|
var exportSettings = {};
|
||||||
|
|
||||||
|
_.each(keys, function(key) {
|
||||||
|
var shouldAllow = _.contains(keysToAllow, key);
|
||||||
|
|
||||||
|
if (!shouldAllow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key.indexOf('color') > -1) {
|
||||||
|
var colorObject = convertColorArrayToObject(settings[key]);
|
||||||
|
settings[key] = colorObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
exportSettings[key] = settings[key];
|
||||||
|
});
|
||||||
|
|
||||||
|
return JSON.stringify(exportSettings, null, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showPreselectedPrompt() {
|
||||||
|
var elem = document.getElementById("exported-props");
|
||||||
|
var exportSettings = prepareSettingsForExport();
|
||||||
|
elem.innerHTML = "";
|
||||||
|
var buttonnode= document.createElement('input');
|
||||||
|
buttonnode.setAttribute('type','button');
|
||||||
|
buttonnode.setAttribute('value','close');
|
||||||
|
elem.appendChild(document.createTextNode("COPY THE BELOW FIELD TO CLIPBOARD:"));
|
||||||
|
elem.appendChild(document.createElement("br"));
|
||||||
|
var textAreaNode = document.createElement("textarea");
|
||||||
|
textAreaNode.value = exportSettings;
|
||||||
|
elem.appendChild(textAreaNode);
|
||||||
|
elem.appendChild(document.createElement("br"));
|
||||||
|
elem.appendChild(buttonnode);
|
||||||
|
|
||||||
|
buttonnode.onclick = function() {
|
||||||
|
console.log("click")
|
||||||
|
elem.innerHTML = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
//window.alert("Ctrl-C to copy, then Enter.", prepareSettingsForExport());
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeContainerDomElement() {
|
||||||
|
var elem = document.getElementById("my-gui-container");
|
||||||
|
elem.parentNode.removeChild(elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeListenerFromGUI(key) {
|
||||||
|
_.each(gui.__listening, function(controller, index) {
|
||||||
|
if (controller.property === key) {
|
||||||
|
storedController = controller;
|
||||||
|
gui.__listening.splice(index, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//the section below is to try to work at achieving two way bindings;
|
||||||
|
|
||||||
|
function addListenersBackToGUI() {
|
||||||
|
gui.__listening.push(storedController);
|
||||||
|
storedController = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerDOMElementsForListenerBlocking() {
|
||||||
|
_.each(gui.__controllers, function(controller) {
|
||||||
|
var input = controller.domElement.childNodes[0];
|
||||||
|
input.addEventListener('focus', function() {
|
||||||
|
console.log('INPUT ELEMENT GOT FOCUS!' + controller.property);
|
||||||
|
removeListenerFromGUI(controller.property);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
_.each(gui.__controllers, function(controller) {
|
||||||
|
var input = controller.domElement.childNodes[0];
|
||||||
|
input.addEventListener('blur', function() {
|
||||||
|
console.log('INPUT ELEMENT GOT BLUR!' + controller.property);
|
||||||
|
addListenersBackToGUI();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// also listen to inputs inside of folders
|
||||||
|
_.each(gui.__folders, function(folder) {
|
||||||
|
_.each(folder.__controllers, function(controller) {
|
||||||
|
var input = controller.__input;
|
||||||
|
input.addEventListener('focus', function() {
|
||||||
|
console.log('FOLDER ELEMENT GOT FOCUS!' + controller.property);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
60
examples/particle_explorer/particleExplorerTool.js
Normal file
60
examples/particle_explorer/particleExplorerTool.js
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
//
|
||||||
|
// particleExplorerTool.js
|
||||||
|
//
|
||||||
|
// Created by Eric Levin on 2/15/16
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
// Adds particleExplorer tool to the edit panel when a user selects a particle entity from the edit tool window
|
||||||
|
// This is an example of a new, easy way to do two way bindings between dynamically created GUI and in-world entities.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
/*global window, alert, EventBridge, dat, listenForSettingsUpdates,createVec3Folder,createQuatFolder,writeVec3ToInterface,writeDataToInterface*/
|
||||||
|
|
||||||
|
|
||||||
|
var PARTICLE_EXPLORER_HTML_URL = Script.resolvePath('particleExplorer.html');
|
||||||
|
|
||||||
|
ParticleExplorerTool = function() {
|
||||||
|
var that = {};
|
||||||
|
|
||||||
|
that.createWebView = function() {
|
||||||
|
var url = PARTICLE_EXPLORER_HTML_URL;
|
||||||
|
that.webView = new OverlayWebWindow({
|
||||||
|
title: 'Particle Explorer',
|
||||||
|
source: url,
|
||||||
|
toolWindow: true
|
||||||
|
});
|
||||||
|
|
||||||
|
that.webView.setVisible(true);
|
||||||
|
that.webView.eventBridge.webEventReceived.connect(that.webEventReceived);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
that.destroyWebView = function() {
|
||||||
|
if (!that.webView) {
|
||||||
|
print("EBL CAN'ZT CLOSE WEB VIEW- IT DOESNT EXISTS!")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("EBL CLOSING WEB VIEW")
|
||||||
|
that.webView.close();
|
||||||
|
that.webView = null;
|
||||||
|
that.activeParticleEntity = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
that.webEventReceived = function(data) {
|
||||||
|
var data = JSON.parse(data);
|
||||||
|
if (data.messageType === "settings_update") {
|
||||||
|
Entities.editEntity(that.activeParticleEntity, data.updatedSettings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
that.setActiveParticleEntity = function(id) {
|
||||||
|
that.activeParticleEntity = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return that;
|
||||||
|
|
||||||
|
|
||||||
|
};
|
Loading…
Reference in a new issue