r3-legacy/src/r3-system-gui.js

986 lines
24 KiB
JavaScript

/**
* System takes care of updating all the entities (based on their component data)
* @param apiSystem R3.API.System
* @constructor
*/
/**
* R3.System.GU
* @constructor
*/
R3.System.GUI = function(options) {
if (R3.Utils.UndefinedOrNull(options)) {
options = {};
}
R3.System.call(this);
if (R3.Utils.UndefinedOrNull(options.guis)) {
options.guis = [];
}
this.guis = options.guis;
this.guiCreatedSubscription = null;
this.guiRemovedSubscription = null;
this.buildGUISubscription = null;
this.clearGUISubscription = null;
this.beforeRenderSubscription = null;
this.removeComponentSubscription = null;
// this.faces = [];
//
// this.exclusiveMode = false;
//
//
// this.meshDeletedSubscription = null;
//
// this.meshSelectedSubscription = null;
//
// this.meshDeselectedSubscription = null;
//
// this.newEntitySubscription = null;
//
// this.meshSelectionObjects = {};
//
// this.sourceChangedSubscription = null;
//
//
//
// this.windowResizeSubscription = null;
//
// this.meshFaceSelectedSubscription = null;
//
// this.meshFaceDeselectedSubscription = null;
};
R3.System.GUI.prototype = Object.create(R3.System.prototype);
R3.System.GUI.prototype.constructor = R3.System.GUI;
R3.System.GUI.prototype.start = function() {
this.guiCreatedSubscription = R3.Event.Subscribe(
R3.Event.GUI_CREATED,
this.guiCreated.bind(this)
);
this.guiRemovedSubscription = R3.Event.Subscribe(
R3.Event.GUI_REMOVED,
this.guiRemoved.bind(this)
);
this.buildGUISubscription = R3.Event.Subscribe(
R3.Event.BUILD_GUI,
this.buildGUI.bind(this)
);
this.clearGUISubscription = R3.Event.Subscribe(
R3.Event.CLEAR_GUI,
this.clearGUI.bind(this)
);
this.beforeRenderSubscription = R3.Event.Subscribe(
R3.Event.BEFORE_RENDER,
this.beforeRender.bind(this)
);
this.removeComponentSubscription = R3.Event.Subscribe(
R3.Event.REMOVE_COMPONENT,
this.removeComponent.bind(this)
);
R3.System.prototype.start.call(this);
// this.windowResizeSubscription = R3.Event.Subscribe(
// R3.Event.WINDOW_RESIZE,
// this.windowResize.bind(this)
// );
//
// this.meshFaceSelectedSubscription = R3.Event.Subscribe(
// R3.Event.MESH_FACE_SELECTED,
// this.meshFaceSelected.bind(this)
// );
//
// this.meshFaceDeselectedSubscription = R3.Event.Subscribe(
// R3.Event.MESH_FACE_DESELECTED,
// this.meshFaceDeselected.bind(this)
// );
//
// this.guis = R3.EntityManager.Instance.findComponentsByConstructor(R3.GUI);
// this.guis.map(
// function(gui){
// this.initialize(gui);
// }.bind(this)
// );
// this.meshDeletedSubscription = R3.Event.Subscribe(
// R3.Event.REMOVE_MESH,
// this.meshDeleted.bind(this)
// );
//
// this.meshSelectedSubscription = R3.Event.Subscribe(
// R3.Event.MESH_SELECTED,
// this.meshSelected.bind(this)
// );
//
// this.meshDeselectedSubscription = R3.Event.Subscribe(
// R3.Event.MESH_DESELECTED,
// this.meshDeslected.bind(this)
// );
//
// this.componentRemovedSubscription = R3.Event.Subscribe(
// R3.Event.REMOVE_COMPONENT,
// this.removeComponent.bind(this)
// );
//
// this.sourceChangedSubscription = R3.Event.Subscribe(
// R3.Event.CAST_SOURCE_CHANGED,
// this.castSourceChanged.bind(this)
// );
//
// this.destinationChangedSubscription = R3.Event.Subscribe(
// R3.Event.RECEIVE_DESTINATION_CHANGED,
// this.receiveDestinationChanged.bind(this)
// );
};
// R3.System.GUI.prototype.windowResize = function(data) {
// };
R3.System.GUI.prototype.beforeRender = function() {
this.guis.map(
function(gui) {
if (typeof gui.instance.update === 'function') {
gui.instance.update();
}
}
);
};
R3.System.GUI.prototype.removeComponent = function(data) {
this.guis.map(
function(gui) {
gui.instance._panels.map(
function(panel) {
if (panel._label === data.component.name) {
panel.getNode().getElement().parentElement.removeChild(panel.getNode().getElement())
}
}
)
}
);
};
R3.System.GUI.prototype.guiCreated = function(gui) {
R3.Utils.PushUnique(this.guis, gui);
};
R3.System.GUI.prototype.guiRemoved = function(gui) {
gui.dispose();
var index = this.guis.indexOf(gui);
if (index === -1) {
console.warn('gui system out of sync');
} else {
this.guis.splice(index, 1);
}
};
// /**
// * Push the mesh to our backup components, if in exclusiveMode (menu at top is selected),
// * otherwise, just to our normal components
// * @param data
// */
// R3.System.GUI.prototype.meshSelected = function(data) {
//
// if (this.exclusiveMode) {
// R3.Utils.PushUnique(this.backupComponents, data.mesh);
// } else {
// R3.Utils.PushUnique(this.components, data.mesh);
// }
//
// };
//
// /**
// * Same as selected above, but removes the mesh from the components
// * @param data
// */
// R3.System.GUI.prototype.meshDeslected = function(data) {
//
// var index = -1;
//
// if (this.exclusiveMode) {
// index = this.backupComponents.indexOf(data.mesh);
// if (index !== -1) {
// this.backupComponents.splice(index, 1);
// }
// } else {
// index = this.components.indexOf(data.mesh);
// if (index !== -1) {
// this.components.splice(index, 1);
// }
// }
//
// };
//
// R3.System.GUI.prototype.meshFaceSelected = function(data) {
//
// this.faces.push(data.face);
//
// this.buildGUI({
// components : this.faces
// })
// };
//
// R3.System.GUI.prototype.meshFaceDeselected = function(data) {
//
// var index = this.faces.indexOf(data.face);
//
// if (index !== -1) {
// this.faces.splice(index, 1);
//
// if (this.faces.length === 0) {
// this.buildGUI({});
// } else {
// this.buildGUI({components : this.faces})
// }
//
// } else {
// console.warn('could not remove face');
// }
// };
/**
* This function responds to the BUILD_GUI event, data contains the components to build a GUI for data.
*
* If we send data with components - go into exclusive mode, backup the currently selected components and rebuild the gui
*
* If we send data without components - go out of exclusive mode, restore the backup of the currently selected components
* and rebuild based on the meshes
*
* If we don't send any data (null or undefined) - some parameters changed and just rebuild the gui.
*
* @param data
*/
/**
* clear GUI
*/
R3.System.GUI.prototype.clearGUI = function() {
this.guis.map(
function(gui) {
gui.clear();
}
);
};
R3.System.GUI.prototype.buildGUI = function(component) {
this.guis.map(
function(gui) {
/**
* First, start fresh - remove all folders
*/
gui.clear();
var addComponent = function(group, component, property) {
if (
component.hasOwnProperty(property) ||
typeof component[property] === 'function'
) {
if (
property === 'componentType' ||
property === 'register' ||
property === 'linked' ||
property === 'loaded' ||
property === 'selected'
) {
return;
}
if (component.guiInfo && component.guiInfo[property] && component.guiInfo[property].options) {
gui.addSelect(group, component, property);
return;
}
if (component[property] instanceof R3.Color) {
gui.addColor(group, component, property);
return;
}
if (typeof component[property] === 'boolean') {
gui.addCheckbox(group, component, property);
return;
}
if (typeof component[property] === 'number') {
gui.addNumber(group, component, property);
return;
}
if (typeof component[property] === 'string') {
gui.addString(group, component, property);
return;
}
if (typeof component[property] === 'function') {
gui.addButton(group, component, property);
return;
}
}
};
var panel = gui.addPanel(component.name);
gui.addGroup(panel, 'Main');
var traverseComponent = function(component) {
var properties = Object.keys(component);
properties.sort();
properties.splice(properties.indexOf('id'), 1);
properties.splice(properties.indexOf('name'), 1);
properties.unshift('name');
properties.unshift('id');
properties.map(
function(property) {
if (component.hasOwnProperty(property)) {
if (component.guiInfo && component.guiInfo.hasOwnProperty(property)) {
/**
* Skip properties in guiInfos - they need to go into subgroups
*/
return;
}
addComponent(panel, component, property);
}
}
);
properties.map(
function(property) {
if (component.hasOwnProperty(property)) {
if (!(component.guiInfo && component.guiInfo.hasOwnProperty(property))) {
/**
* Skip properties *NOT* in guiInfos
*/
return;
}
var group = panel.addSubGroup(
{
label : property
}
);
addComponent(group, component, property);
}
}
);
};
traverseComponent(component);
Object.keys(component.idToObject).map(
function(id) {
var _component = R3.EntityManager.Instance.findComponentById(id);
if (_component.id === component.id) {
return;
}
if (_component instanceof R3.Color) {
return;
}
gui.addGroup(panel, _component.name);
traverseComponent(_component);
}
);
var functions = [];
for (var fn in component) {
if (
typeof component[fn] === 'function') {
if (
// fn === 'clone' ||
fn === 'remove' ||
fn === 'save'
// fn === 'saveToRemoteAPI'
) {
functions.push(fn);
}
}
}
functions.sort();
var actions = gui.addGroup(panel, 'Actions');
functions.map(
function(fn) {
addComponent(actions, component, fn);
}
)
// var guiInfos = Object.keys(__component.guiInfo);
//
// var linkedObjects = Object.keys(__component.idToObject);
//
//
//
//
// .reduce(
// function(result, key) {
//
// var
//
// result.push(key);
// return result;
// },
// []
// );
//
// var numberGroup = panel.addGroup(
// {
// label : 'Numbers'
// }
// );
//
// var stringGroup = panel.addGroup(
// {
// label : 'Strings'
// }
// );
//
//
// var addComponent = function(group, component) {
// return function(property) {
// if (component.hasOwnProperty(property)) {
//
// if (typeof component[property] === 'number') {
// gui.addNumber(numberGroup, component, property)
// }
//
// if (typeof component[property] === 'string') {
// gui.addString(stringGroup, component, property)
// }
//
// if (typeof component[property] === 'function') {
// gui.addButton(group, component, property)
// }
// }
// };
// };
//
// Object.keys(__component).map(addComponent(panel, __component));
//
// Object.keys(__component.idToObject).map(
// function(id) {
//
// if (id === __component.id) {
// return;
// }
//
// var component = R3.EntityManager.Instance.findComponentById(id);
//
// var group = gui.addGroup(panel, component.name);
//
// Object.keys(component).map(addComponent(group, component));
// }
// );
}.bind(this)
);
return;
this.guis.map(function(gui){
/**
* First, start fresh - remove all folders
*/
gui.instance.destroy();
gui.removeAllFolders();
// gui.domElement.instance.parentElement.appendChild(gui.instance.domElement);
if (data) {
if (data.components) {
/**
* Check if we are not already in exclusive mode, because we only want to make a backup if
* it does not already exist
*/
if (!this.exclusiveMode) {
this.exclusiveMode = true;
/**
* Backup the current selection
*/
this.backupComponents = this.components.map(
function(component) {
return component;
}
);
}
this.components = data.components.map(
function(component) {
return component;
}
);
} else {
if (this.exclusiveMode) {
this.exclusiveMode = false;
/**
* Time to restore the backup
*/
this.components = this.backupComponents.map(
function(component) {
return component;
}
)
} else {
console.log('we are already not in mesh select mode - not doing anything with the backup');
}
}
}
/**
* For all mesh components - (if not in exclusive mode) - try to discover all materials, textures, etc
*/
if (!this.exclusiveMode) {
/**
* We first remove everything which is not a Mesh
*/
this.components = this.components.filter(
function(component) {
return (component instanceof R3.D3.Mesh);
}
);
}
/**
* Check if we have components to build a GUI for
*/
if (R3.Utils.UndefinedOrNull(this.components.length) || this.components.length < 1) {
// console.log('no components selected');
return;
}
/**
* Now continue to discover materials, textures, images etc. children of this component
*/
if (!this.exclusiveMode) {
this.components = this.components.reduce(
function(result, component) {
var components = component.getChildrenComponents();
R3.Utils.PushUnique(result, component);
components.map(function(component){
R3.Utils.PushUnique(result, component);
});
return result;
}.bind(this),
[]
);
}
/**
* Sort the components by component type
*/
this.components.sort(
function(a, b) {
if (a.componentType > b.componentType) {
return 1;
}
if (a.componentType < b.componentType) {
return -1;
}
return 0;
}
);
/**
* Split the components into groups of componentType
*/
var componentGroups = this.components.reduce(
function(result, component) {
var componentData = result.pop();
if (component.componentType === componentData.componentType) {
/**
* This is the first component
*/
componentData.components.push(component);
result.push(componentData);
return result;
}
if (component.componentType !== componentData.componentType) {
result.push(componentData);
result.push({
componentType : component.componentType,
components:[component]
});
return result;
}
},
[
{
componentType : this.components[0].componentType,
components : []
}
]
);
/**
* We have all the components split into groups - now add the individual components
*/
this.components.map(
function(component) {
/**
* Check for possible duplicates
* @type {boolean}
*/
var duplicate = false;
componentGroups.map(function(componentGroup){
if (
componentGroup.componentType === component.componentType &&
componentGroup.components.length === 1 &&
componentGroup.components[0] === component
) {
duplicate = true;
}
});
if (!duplicate) {
R3.Utils.PushUnique(
componentGroups,
{
componentType : component.componentType,
components : [component]
}
);
}
}
);
/**
* componentGroups should now contain the whole list of stuff we want to build GUI for.
*/
componentGroups.map(
function(componentGroup){
if (componentGroup.components.length < 1) {
console.warn('invalid number of components');
}
var templateObject = {
template : {
/**
* Doing this here is just to put parent at the top of the gui
*/
'parent' : componentGroup.components[0].parent
},
affected : [componentGroup.components[0]],
componentType : componentGroup.componentType
};
for (var property in componentGroup.components[0]) {
if (
componentGroup.components[0].hasOwnProperty(property) ||
typeof componentGroup.components[0][property] === 'function'
) {
if (typeof componentGroup.components[0][property] === 'function') {
templateObject.template[property] = function(__property) {
return function() {
this.affected.map(
function(component) {
component[__property].bind(component)();
}
)
}.bind(templateObject);
}(property);
} else {
templateObject.template[property] = componentGroup.components[0][property];
}
}
}
var componentTemplate = componentGroup.components.reduce(
function(result, component) {
if (component === componentGroup.components[0]) {
/**
* This is the first component, just return
*/
return result;
}
/**
* Now start to filter out the properties
*/
for (var property in component) {
if (
component.hasOwnProperty(property)
) {
if (!result.template.hasOwnProperty(property)) {
continue;
}
if (
result.template[property] instanceof R3.Vector2 ||
result.template[property] instanceof R3.Vector3 ||
result.template[property] instanceof R3.Vector4 ||
result.template[property] instanceof R3.Quaternion
) {
if (!result.template[property].equals(component[property])) {
delete result.template[property];
}
continue;
}
if (result.template[property] !== component[property]) {
delete result.template[property];
}
}
}
/**
* Store the affected component
*/
result.affected.push(component);
return result;
},
templateObject
);
/**
* componentTemplate now contains for this particular component type group - all
* the properties which are modifiable, and also the objects affected by this property changes
*/
var name;
if (R3.Utils.UndefinedOrNull(componentTemplate.template.name)) {
name = R3.GetComponentName(componentTemplate) + ' (All Selected (' + componentTemplate.affected.length + '))';
} else {
name = componentTemplate.template.name;
}
var folder = gui.addFolder(name);
if (!folder) {
return;
}
for (var templateProperty in componentTemplate.template) {
if (
componentTemplate.template.hasOwnProperty(templateProperty) ||
typeof (componentTemplate.template[templateProperty]) === 'function'
) {
if (typeof (componentTemplate.template[templateProperty]) === 'function') {
folder.add(componentTemplate.template, templateProperty);
continue;
}
/**
* We only want to affect runtime vectors because their onchange will execute updateInstance()
*/
if (
componentTemplate.template[templateProperty] instanceof R3.Vector2 ||
componentTemplate.template[templateProperty] instanceof R3.Vector3 ||
componentTemplate.template[templateProperty] instanceof R3.Vector4
) {
this.buildVectorControl(folder, componentTemplate, templateProperty);
continue;
}
if (componentTemplate.template[templateProperty] instanceof R3.Quaternion) {
this.buildQuaternionControl(folder, componentTemplate, templateProperty);
}
if (
templateProperty.indexOf('parent') === 0
) {
if (componentTemplate.template[templateProperty] instanceof Array) {
console.warn('read-only property :' + templateProperty);
} else {
this.buildParentSelectionControl(folder, componentTemplate, templateProperty);
}
continue;
}
if (componentTemplate.template[templateProperty] instanceof Array) {
if (
templateProperty === 'vertices' ||
templateProperty === 'faces'
) {
continue;
}
if (templateProperty === 'uvs') {
this.buildUVManagerControl(folder, componentTemplate, templateProperty);
}
if (
componentTemplate.template.linkedComponents &&
componentTemplate.template.linkedComponents[templateProperty] instanceof Array
) {
this.buildArrayManagerControl(folder, componentTemplate, templateProperty);
}
continue;
}
if (componentTemplate.template[templateProperty] instanceof R3.Color) {
this.buildColorControl(folder, componentTemplate, templateProperty);
continue;
}
if (typeof componentTemplate.template[templateProperty] === 'object') {
if (
componentTemplate.template[templateProperty] instanceof R3.Component ||
(
componentTemplate.template.linkedComponents &&
componentTemplate.template.linkedComponents[templateProperty]
)
) {
this.buildSelectControl(folder, componentTemplate, templateProperty)
} else {
this.buildObjectControl(folder, componentTemplate, templateProperty);
}
continue;
}
this.buildControl(folder, componentTemplate, templateProperty);
}
}
}.bind(this)
);
}.bind(this));
};
// R3.System.GUI.prototype.meshDeleted = function(data) {
//
// data.meshes.map(function(mesh){
// this.meshDeslected({
// mesh : mesh
// })
// }.bind(this));
//
// this.buildGUI(null);
// };
//
// R3.System.GUI.prototype.removeComponent = function(data) {
//
// var index = this.backupComponents.indexOf(data.component);
// if (index !== -1) {
// this.backupComponents.splice(index, 1);
// }
//
// index = this.components.indexOf(data.component);
// if (index !== -1) {
// this.components.splice(index, 1);
// }
//
// };
//
// R3.System.GUI.prototype.castSourceChanged = function(data) {
// this.buildGUI(null);
// };
//
// R3.System.GUI.prototype.receiveDestinationChanged = function(data) {
// this.buildGUI(null);
// };
R3.System.GUI.prototype.stop = function() {
this.guis.map(
function(gui) {
gui.dispose();
}
);
this.guiCreatedSubscription.remove();
this.guiRemovedSubscription.remove();
this.buildGUISubscription.remove();
this.clearGUISubscription.remove();
this.beforeRenderSubscription.remove();
this.removeComponentSubscription.remove();
R3.System.prototype.stop.call(this);
};