/** * System takes care of updating all the entities (based on their component data) * @param apiSystem GameLib.API.System * @constructor */ GameLib.System.GUI = function( apiSystem ) { GameLib.System.call( this, apiSystem ); this.guis = []; this.components = []; /** * When we want to show a specific set of components - we backup the current components and then restore them * later when we are done. * @type {null} */ this.backupComponents = null; this.exclusiveMode = false; this.buildGUISubscription = null; this.meshDeletedSubscription = null; this.meshSelectedSubscription = null; this.meshDeselectedSubscription = null; this.newEntitySubscription = null; this.meshSelectionObjects = {}; }; GameLib.System.GUI.prototype = Object.create(GameLib.System.prototype); GameLib.System.GUI.prototype.constructor = GameLib.System.GUI; GameLib.System.GUI.prototype.start = function() { this.guis = GameLib.EntityManager.Instance.queryComponents(GameLib.GUI); /** * Add some GUI behaviour */ dat.GUI.prototype.removeEmtpyFolders = function() { for (var property in this.__folders) { if (this.__folders.hasOwnProperty(property)){ var folder = this.__folders[property]; if (folder.__listening.length === 0) { folder.close(); this.__ul.removeChild(folder.domElement.parentNode); delete this.__folders[property]; this.onResize(); } } } }; dat.GUI.prototype.removeAllFolders = function() { for (var property in this.__folders) { if (this.__folders.hasOwnProperty(property)){ var folder = this.__folders[property]; folder.close(); this.__ul.removeChild(folder.domElement.parentNode); delete this.__folders[property]; this.onResize(); } } }; this.guis.map(function(gui){ gui.domElement.instance.parentElement.appendChild(gui.instance.domElement); }); this.buildGUISubscription = this.subscribe( GameLib.Event.BUILD_GUI, this.buildGUI ); this.meshDeletedSubscription = this.subscribe( GameLib.Event.MESH_DELETED, this.meshDeleted ); this.meshSelectedSubscription = this.subscribe( GameLib.Event.MESH_SELECTED, this.meshSelected ); this.meshDeselectedSubscription = this.subscribe( GameLib.Event.MESH_DESELECTED, this.meshDeslected ); this.newEntitySubscription = this.subscribe( GameLib.Event.NEW_ENTITY, this.newEntity ); this.componentRemovedSubscription = this.subscribe( GameLib.Event.COMPONENT_REMOVE, this.removeComponent ) }; GameLib.System.GUI.prototype.buildVectorControl = function(folder, templateObject, property, dimension) { var step = 0.1; var object = templateObject.template; var affected = templateObject.affected; if ( property === 'localRotation' || property === 'localPosition' ) { step = 0.001; } if (dimension === 4) { folder.add( object[property], 'w', -100, 100, step ).name(property + '.w').listen().onChange( function(value) { affected.map(function(component){ component[property].w = value; component.updateInstance(); }.bind(this)); } ); } folder.add( object[property], 'x', -100, 100, step ).name(property + '.x').listen().onChange( function(value) { affected.map(function(component){ component[property].x = value; component.updateInstance(); }.bind(this)); } ); folder.add( object[property], 'y', -100, 100, step ).name(property + '.y').listen().onChange( function(value) { affected.map(function(component){ component[property].y = value; component.updateInstance(); }.bind(this)); } ); if ( dimension === 3 || dimension === 4 ) { folder.add( object[property], 'z', -100, 100, step ).name(property + '.z').listen().onChange( function(value) { affected.map(function(component){ component[property].z = value; component.updateInstance(); }.bind(this)); } ); } }; /** * Builds an Entity Selection control * @param folder * @param componentTemplate */ GameLib.System.GUI.prototype.buildEntitySelectionControl = function(folder, componentTemplate) { var options = GameLib.EntityManager.Instance.queryComponents(GameLib.Entity).reduce( function(result, entity) { result[entity.name] = entity; return result; }, { 'none' : null } ); var object = componentTemplate.template; var affected = componentTemplate.affected; folder.add(object, 'parentEntity', options).listen().onChange( function(value) { var newEntity = null; if (value !== 'null') { newEntity = GameLib.EntityManager.Instance.findEntityById(value); } affected.map( function(component) { component.parentEntity = newEntity; GameLib.Event.Emit( GameLib.Event.PARENT_ENTITY_CHANGE, { originalEntity : this.initialValue, newEntity : newEntity, object : component } ); }.bind(this) ); this.initialValue = newEntity; } ).onFinishChange( function(){ // GameLib.Event.Emit( // GameLib.Event.BUILD_GUI, // { // // } // ) } ); }; GameLib.System.GUI.prototype.buildArrayManagerControl = function( folder, componentTemplate, property, constructor ) { console.log('TODO: array manager control'); // var array = object[property]; // // var addArrayItem = function(item, index){ // // var controller = folder.add( // { // 'remove' : function() { // object[property].splice(object[property].indexOf(item), 1); // folder.remove(controller); // } // }, // 'remove' // ).name('remove ' + property + '[' + index + '] - ' + item.name); // }; // // array.map(addArrayItem); // // var idToObject = {}; // // var selectionObject = entityManager.queryComponents(constructor).reduce( // function(result, component) { // result[component.name] = component; // idToObject[component.id] = component; // return result; // }, // { // 'none' : null // } // ); // // var activeSelection = function(__object, __property) { // // var object = __object; // var property = __property; // // return { // component : null, // add : function() { // if (object[property].indexOf(activeSelection.component) === -1) { // object[property].push(activeSelection.component); // addArrayItem(activeSelection.component, object[property].length - 1) // } // } // }; // // }(object, property); // // folder.add(activeSelection, 'component', selectionObject).name('select ' + property).onChange( // function(value){ // if (value === 'null') { // activeSelection['component'] = null; // } else { // activeSelection['component'] = idToObject[value]; // } // } // ).listen(); // // folder.add(activeSelection, 'add').name('add to ' + property); }; GameLib.System.GUI.prototype.buildSelectControl = function(folder, object, property, entityManager, constructor) { var objects = entityManager.queryComponents(constructor); var idToObject = {}; var options = objects.reduce( function(result, obj) { result[obj.name] = obj; idToObject[obj.id] = obj; return result; }, { 'none' : null } ); folder.add( object, property, options ).name(property).listen().onChange( function(gui) { return function (value) { if (value !== 'null') { object[property] = idToObject[value]; } else { object[property] = null; } if (property === 'parentScene') { /** * New way of doing things */ GameLib.Event.Emit( GameLib.Event.PARENT_SCENE_CHANGE, { originalScene: this.initialValue, newScene: object[property], object: object, entityManager: entityManager } ); } else { /** * Old way of doing things */ object.updateInstance(); } /** * Properties changed - rebuild GUI */ //gui.build(entityManager); }; }(this) ); }; GameLib.System.GUI.prototype.buildControl = function(folder, componentTemplate, property) { var object = componentTemplate.template; var componentType = componentTemplate.componentType; var handles = []; if ( GameLib.Utils.isString(object[property]) || GameLib.Utils.isBoolean(object[property]) ) { handles.push(folder.add(object, property).name(property).listen()); } if (GameLib.Utils.isNumber(object[property])) { var grain = 0.001; if (object.grain) { grain = object.grain; } if (property === 'systemType') { handles.push( folder.add( object, property, { 'animation' : GameLib.System.SYSTEM_TYPE_ANIMATION, 'gui' : GameLib.System.SYSTEM_TYPE_GUI, 'input' : GameLib.System.SYSTEM_TYPE_INPUT, 'render' : GameLib.System.SYSTEM_TYPE_RENDER, 'storage' : GameLib.System.SYSTEM_TYPE_STORAGE, 'linking' : GameLib.System.SYSTEM_TYPE_LINKING, 'physics' : GameLib.System.SYSTEM_TYPE_PHYSICS } ).listen() ); } else if (property === 'broadphaseType') { handles.push( folder.add( object, property, { 'naive': GameLib.D3.Broadphase.BROADPHASE_TYPE_NAIVE, 'grid': GameLib.D3.Image.BROADPHASE_TYPE_GRID, 'sap': GameLib.D3.Image.BROADPHASE_TYPE_SAP } ).listen() ); } else if (property === 'meshType') { handles.push( folder.add( object, property, { 'normal' : GameLib.D3.Mesh.MESH_TYPE_NORMAL, 'curve' : GameLib.D3.Mesh.MESH_TYPE_CURVE, 'skinned' : GameLib.D3.Mesh.MESH_TYPE_SKINNED, 'plane' : GameLib.D3.Mesh.MESH_TYPE_PLANE, 'sphere' : GameLib.D3.Mesh.MESH_TYPE_SPHERE } ).listen() ); } else if (property === 'materialType') { handles.push( folder.add( object, property, { 'standard': GameLib.D3.Material.MATERIAL_TYPE_STANDARD, 'basic': GameLib.D3.Material.MATERIAL_TYPE_BASIC, 'phong': GameLib.D3.Material.MATERIAL_TYPE_PHONG, 'points': GameLib.D3.Material.MATERIAL_TYPE_POINTS } ).listen() ); } else if (property === 'side') { handles.push( folder.add( object, property, { 'double': GameLib.D3.Material.TYPE_DOUBLE_SIDE, 'front': GameLib.D3.Material.TYPE_FRONT_SIDE, 'back': GameLib.D3.Material.TYPE_BACK_SIDE } ).listen() ); } else if (property === 'combine') { handles.push( folder.add( object, property, { 'multiply': GameLib.D3.Material.TYPE_MULTIPLY_OPERATION, 'mix': GameLib.D3.Material.TYPE_MIX_OPERATION, 'add': GameLib.D3.Material.TYPE_ADD_OPERATION } ).listen() ); } else if (property === 'vertexColors') { handles.push( folder.add( object, property, { 'none': GameLib.D3.Material.TYPE_NO_COLORS, 'face': GameLib.D3.Material.TYPE_FACE_COLORS, 'vertex': GameLib.D3.Material.TYPE_VERTEX_COLORS } ).listen() ); } else if (property === 'blending') { handles.push( folder.add( object, property, { 'normal': GameLib.D3.Material.TYPE_NORMAL_BLENDING, 'additive': GameLib.D3.Material.TYPE_ADDITIVE_BLENDING, 'subtractive': GameLib.D3.Material.TYPE_SUBTRACTIVE_BLENDING, 'multiply': GameLib.D3.Material.TYPE_MULTIPLY_BLENDING } ).listen() ); } else if (property === 'blendSrc') { handles.push( folder.add( object, property,- { 'zero': GameLib.D3.Material.TYPE_ZERO_FACTOR, 'one': GameLib.D3.Material.TYPE_ONE_FACTOR, 'source color': GameLib.D3.Material.TYPE_SRC_COLOR_FACTOR, 'one minus source color': GameLib.D3.Material.TYPE_ONE_MINUS_SRC_COLOR_FACTOR, 'source alpha': GameLib.D3.Material.TYPE_SRC_ALPHA_FACTOR, 'one minus source alpha': GameLib.D3.Material.TYPE_ONE_MINUS_SRC_ALPHA_FACTOR, 'destination alpha': GameLib.D3.Material.TYPE_DST_ALPHA_FACTOR, 'one minus destination alpha': GameLib.D3.Material.TYPE_ONE_MINUS_DST_ALPHA_FACTOR, 'destination color': GameLib.D3.Material.TYPE_DST_COLOR_FACTOR, 'one minus destination color': GameLib.D3.Material.TYPE_ONE_MINUS_DST_COLOR_FACTOR, 'source alpha saturate': GameLib.D3.Material.TYPE_SRC_ALPHA_SATURATE_FACTOR } ).listen() ); } else if (property === 'blendDst') { handles.push( folder.add( object, property, { 'zero': GameLib.D3.Material.TYPE_ZERO_FACTOR, 'one': GameLib.D3.Material.TYPE_ONE_FACTOR, 'source color': GameLib.D3.Material.TYPE_SRC_COLOR_FACTOR, 'one minus source color': GameLib.D3.Material.TYPE_ONE_MINUS_SRC_COLOR_FACTOR, 'source alpha': GameLib.D3.Material.TYPE_SRC_ALPHA_FACTOR, 'one minus source alpha': GameLib.D3.Material.TYPE_ONE_MINUS_SRC_ALPHA_FACTOR, 'destination alpha': GameLib.D3.Material.TYPE_DST_ALPHA_FACTOR, 'one minus destination alpha': GameLib.D3.Material.TYPE_ONE_MINUS_DST_ALPHA_FACTOR, 'destination color': GameLib.D3.Material.TYPE_DST_COLOR_FACTOR, 'one minus destination color': GameLib.D3.Material.TYPE_ONE_MINUS_DST_COLOR_FACTOR, 'source alpha saturate': GameLib.D3.Material.TYPE_SRC_ALPHA_SATURATE_FACTOR } ).listen() ); } else if (property === 'blendEquation') { handles.push( folder.add( object, property, { 'add': GameLib.D3.Material.TYPE_ADD_EQUATION, 'subtract': GameLib.D3.Material.TYPE_SUBTRACT_EQUATION, 'reverse subtract': GameLib.D3.Material.TYPE_REVERSE_SUBTRACT_EQUATION, 'min': GameLib.D3.Material.TYPE_MIN_EQUATION, 'max': GameLib.D3.Material.TYPE_MAX_EQUATION } ).listen() ); } else if (property === 'depthFunc') { handles.push( folder.add( object, property, { 'never': GameLib.D3.Material.TYPE_NEVER_DEPTH, 'always': GameLib.D3.Material.TYPE_ALWAYS_DEPTH, 'less depth': GameLib.D3.Material.TYPE_LESS_DEPTH, 'less equal depth': GameLib.D3.Material.TYPE_LESS_EQUAL_DEPTH, 'equal depth': GameLib.D3.Material.TYPE_EQUAL_DEPTH, 'greated equal depth': GameLib.D3.Material.TYPE_GREATER_EQUAL_DEPTH, 'greated depth': GameLib.D3.Material.TYPE_GREATER_DEPTH, 'not equal depth': GameLib.D3.Material.TYPE_NOT_EQUAL_DEPTH } ).listen() ); } else if (property === 'wrapS') { handles.push( folder.add( object, property, { 'repeat': GameLib.D3.Texture.TYPE_REPEAT_WRAPPING, 'clamp': GameLib.D3.Texture.TYPE_CLAMP_TO_EDGE_WRAPPING, 'mirrored repeat': GameLib.D3.Texture.TYPE_MIRRORED_REPEAT_WRAPPING } ).listen() ); } else if (property === 'wrapT') { handles.push( folder.add( object, property, { 'repeat': GameLib.D3.Texture.TYPE_REPEAT_WRAPPING, 'clamp': GameLib.D3.Texture.TYPE_CLAMP_TO_EDGE_WRAPPING, 'mirrored repeat': GameLib.D3.Texture.TYPE_MIRRORED_REPEAT_WRAPPING } ).listen() ); } else if (property === 'format') { handles.push( folder.add( object, property, { 'alpha': GameLib.D3.Texture.TYPE_ALPHA_FORMAT, 'rgb': GameLib.D3.Texture.TYPE_RGB_FORMAT, 'rgba': GameLib.D3.Texture.TYPE_RGBA_FORMAT, 'luminance': GameLib.D3.Texture.TYPE_LUMINANCE_FORMAT, 'luminance alpha': GameLib.D3.Texture.TYPE_LUMINANCE_ALPHA_FORMAT, 'depth': GameLib.D3.Texture.TYPE_DEPTH_FORMAT } ).listen() ); } else if (property === 'mapping') { handles.push( folder.add( object, property, { 'uv': GameLib.D3.Texture.TYPE_UV_MAPPING, 'cube reflection': GameLib.D3.Texture.TYPE_CUBE_REFLECTION_MAPPING, 'cube refraction': GameLib.D3.Texture.TYPE_CUBE_REFRACTION_MAPPING, 'equi rectangular reflection': GameLib.D3.Texture.TYPE_EQUI_RECTANGULAR_REFLECTION_MAPPING, 'equi rectangular refraction': GameLib.D3.Texture.TYPE_EQUI_RECTANGULAR_REFRACTION_MAPPING, 'spherical reflection': GameLib.D3.Texture.TYPE_SPHERICAL_REFLECTION_MAPPING, 'cube uv reflection': GameLib.D3.Texture.TYPE_CUBE_UV_REFLECTION_MAPPING, 'cube uv refraction': GameLib.D3.Texture.TYPE_CUBE_UV_REFRACTION_MAPPING } ).listen() ); } else if (property === 'magFilter') { handles.push( folder.add( object, property, { 'nearest': GameLib.D3.Texture.TYPE_NEAREST_FILTER, 'nearest mipmap nearest': GameLib.D3.Texture.TYPE_NEAREST_MIPMAP_NEAREST_FILTER, 'nearest mipmap linear': GameLib.D3.Texture.TYPE_NEAREST_MIPMAP_LINEAR_FILTER, 'linear': GameLib.D3.Texture.TYPE_LINEAR_FILTER, 'linear mipmap nearest': GameLib.D3.Texture.TYPE_LINEAR_MIPMAP_NEAREST_FILTER, 'linear mipmap linear': GameLib.D3.Texture.TYPE_LINEAR_MIPMAP_LINEAR_FILTER } ).listen() ); } else if (property === 'minFilter') { handles.push(folder.add( object, property, { 'nearest': GameLib.D3.Texture.TYPE_NEAREST_FILTER, 'nearest mipmap nearest': GameLib.D3.Texture.TYPE_NEAREST_MIPMAP_NEAREST_FILTER, 'nearest mipmap linear': GameLib.D3.Texture.TYPE_NEAREST_MIPMAP_LINEAR_FILTER, 'linear': GameLib.D3.Texture.TYPE_LINEAR_FILTER, 'linear mipmap nearest': GameLib.D3.Texture.TYPE_LINEAR_MIPMAP_NEAREST_FILTER, 'linear mipmap linear': GameLib.D3.Texture.TYPE_LINEAR_MIPMAP_LINEAR_FILTER } ).listen() ); } else if (componentType === GameLib.Component.COMPONENT_TEXTURE && property === 'typeId') { handles.push( folder.add( object, property, { 'normal': GameLib.D3.Texture.TEXTURE_TYPE_NORMAL, 'cube': GameLib.D3.Texture.TEXTURE_TYPE_CUBE } ).listen() ); } else if (property === 'textureType') { handles.push( folder.add( object, property, { 'unsigned byte': GameLib.D3.Texture.TYPE_UNSIGNED_BYTE, 'byte': GameLib.D3.Texture.TYPE_BYTE, 'short': GameLib.D3.Texture.TYPE_SHORT, 'unsigned short': GameLib.D3.Texture.TYPE_UNSIGNED_SHORT, 'int': GameLib.D3.Texture.TYPE_INT, 'unsigned int': GameLib.D3.Texture.TYPE_UNSIGNED_INT, 'float': GameLib.D3.Texture.TYPE_FLOAT, 'half float': GameLib.D3.Texture.TYPE_HALF_FLOAT } ).listen() ); } else if (property === 'encoding') { handles.push( folder.add( object, property, { 'linear': GameLib.D3.Texture.TYPE_LINEAR_ENCODING, 'srgb': GameLib.D3.Texture.TYPE_SRGB_ENCODING, 'gamma': GameLib.D3.Texture.TYPE_GAMMA_ENCODING, 'rgbe': GameLib.D3.Texture.TYPE_RGBE_ENCODING, 'log luv': GameLib.D3.Texture.TYPE_LOG_LUV_ENCODING, 'rgbm7': GameLib.D3.Texture.TYPE_RGBM7_ENCODING, 'rgbm16': GameLib.D3.Texture.TYPE_RGBM16_ENCODING, 'rgbd': GameLib.D3.Texture.TYPE_RGBD_ENCODING } ).listen() ); } else if (property === 'lightType') { handles.push( folder.add( object, property, { 'ambient': GameLib.D3.Light.LIGHT_TYPE_AMBIENT, 'directional': GameLib.D3.Light.LIGHT_TYPE_DIRECTIONAL, 'spot': GameLib.D3.Light.LIGHT_TYPE_SPOT, 'point': GameLib.D3.Light.LIGHT_TYPE_POINT } ).listen() ); } else { /** * Try to guess a scale for this property */ if ( property === 'opacity' || property === 'metalness' || property === 'roughness' ) { handles.push(folder.add(object, property, 0, 1.0, 0.001).listen()); } else if ( property === 'shininess' || property === 'fov' ) { handles.push(folder.add(object, property, -255, 255, 1).listen()); } else if ( property === 'aspect' ) { handles.push(folder.add(object, property, 0, 5, 0.001).listen()); } else if ( property === 'widthSegments' || property === 'heightSegments' ) { handles.push(folder.add(object, property, 1, 1000, 1).listen()); } else if ( property === 'angle' || property === 'width' || property === 'height' || property === 'depth' ) { handles.push(folder.add(object, property, -1000, 1000, 1).listen()); } else if ( property === 'near' || property === 'distanceGrain' || property === 'bumpScale' || property === 'envMapIntensity' ) { handles.push(folder.add(object, property, -10, 100, 0.001).listen()); } else if ( property === 'heightOffset' || property === 'rotationFactor' ) { handles.push(folder.add(object, property, -100, 100, 0.001).step(0.001).listen()); } else { handles.push(folder.add(object, property, -10000, 10000, grain).listen()); } } } handles.map( function(handle) { if (property === 'name') { handle.onFinishChange( function(__handle) { return function(value) { componentTemplate.affected.map( function(component){ component[property] = value; component.updateInstance(); } ); } }(handle) ); } else { handle.onChange( function (value) { if (typeof this.initialValue === 'number') { value = Number(value); } componentTemplate.affected.map( function(component){ component[property] = value; component.updateInstance(); } ); } ); } } ); }; /** * Push the mesh to our backup components, if in exclusiveMode (menu at top is selected), * otherwise, just to our normal components * @param data */ GameLib.System.GUI.prototype.meshSelected = function(data) { if (this.exclusiveMode) { GameLib.Utils.PushUnique(this.backupComponents, data.mesh); } else { GameLib.Utils.PushUnique(this.components, data.mesh); } }; /** * Same as selected above, but removes the mesh from the components * @param data */ GameLib.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); } } }; /** * This function responds to the BUILD_GUI event, data contains the components to build a GUI for * @param data */ GameLib.System.GUI.prototype.buildGUI = function(data) { this.guis.map(function(gui){ /** * First, start fresh - remove all folders */ gui.removeAllFolders(); if (data) { if (data.components) { 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 { this.exclusiveMode = false; /** * Time to restore the backup */ this.components = this.backupComponents.map( function(component) { return component; } ) } } /** * 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 GameLib.D3.Mesh); } ); } /** * Check if we have components to build a GUI for */ if (GameLib.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(); GameLib.Utils.PushUnique(result, component); components.map(function(component){ GameLib.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) { GameLib.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 parentEntity at the top of the gui */ 'parentEntity' : componentGroup.components[0].parentEntity }, 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 GameLib.Vector2 || result.template[property] instanceof GameLib.Vector3 || result.template[property] instanceof GameLib.Vector4 ) { 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 (GameLib.Utils.UndefinedOrNull(componentTemplate.template.name)) { name = GameLib.Component.GetComponentName(componentTemplate.componentType) + ' (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; } if (componentTemplate.template[templateProperty] instanceof GameLib.Vector2) { this.buildVectorControl(folder, componentTemplate, templateProperty, 2); continue; } if (componentTemplate.template[templateProperty] instanceof GameLib.Vector3) { this.buildVectorControl(folder, componentTemplate, templateProperty, 3); continue; } if (componentTemplate.template[templateProperty] instanceof GameLib.Vector4) { this.buildVectorControl(folder, componentTemplate, templateProperty, 4); continue; } if (templateProperty === 'parentEntity') { this.buildEntitySelectionControl(folder, componentTemplate); continue; } if (componentTemplate.template[templateProperty] instanceof Array) { this.buildArrayManagerControl(folder, componentTemplate, templateProperty); continue; } if (typeof componentTemplate.template[templateProperty] === 'object') { /** * ignore objects for now */ continue; } this.buildControl(folder, componentTemplate, templateProperty); // if ( // component.linkedObjects && // component.linkedObjects[property] // ) { // if (property === 'parentEntity') { // this.buildEntitySelectionControlFromArray( // folder, // component, // property, // entityManager // ) // } else if (component.linkedObjects[property] instanceof Array) { // this.buildArrayManager( // folder, // component, // property, // component.linkedObjects[property], // entityManager // ) // } else { // this.buildSelectControl(folder, component, property, entityManager, component.linkedObjects[property]); // } // // } else if (typeof (component[property]) === 'object') { // // if (this.isColor(component[property])) { // this.buildControl(folder, component, property); // } else { // //console.log('ignored: ' + property); // } // } else { // this.buildControl(folder, component, property, entityManager); // } } } }.bind(this) ); }.bind(this)); }; GameLib.System.GUI.prototype.meshDeleted = function(data) { data.meshes.map(function(mesh){ this.meshDeslected({ mesh : mesh }) }.bind(this)); this.buildGUI(null); }; GameLib.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); } }; GameLib.System.GUI.prototype.newEntity = function(data) { }; GameLib.System.GUI.prototype.stop = function() { this.guis.map(function(gui){ gui.domElement.instance.parentElement.removeChild(gui.instance.domElement); }); delete dat.GUI.removeEmtpyFolders; delete dat.GUI.removeAllFolders; this.buildGUISubscription.remove(); this.meshDeletedSubscription.remove(); this.meshSelectedSubscription.remove(); this.meshDeselectedSubscription.remove(); this.newEntitySubscription.remove(); this.componentRemovedSubscription.remove(); this.guis = []; };